]> git.pld-linux.org Git - packages/binutils.git/commitdiff
This commit was manufactured by cvs2git to create branch 'AC-branch'. AC-branch
authorcvs2git <feedback@pld-linux.org>
Sat, 10 Oct 2009 18:10:42 +0000 (18:10 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Cherrypick from master 2009-10-10 18:10:42 UTC Arkadiusz Miśkiewicz <arekm@maven.pl> '- up to 2.20.51.0.2':
    binutils-absolute-gnu_debuglink-path.patch -> 1.6
    binutils-build-id.patch -> 1.1
    binutils-discarded.patch -> 1.3
    binutils-flex.patch -> 1.1
    binutils-gasp.patch -> 1.21
    binutils-info.patch -> 1.11
    binutils-libtool-m.patch -> 1.1
    binutils-libtool-relink.patch -> 1.2
    binutils-pt_pax_flags.patch -> 1.14
    binutils-tooldir.patch -> 1.1
    binutils.spec -> 1.315
Cherrypick from master 2004-11-23 23:30:06 UTC Jakub Bogusz <qboosh@pld-linux.org> '- still needed - updated for 2.15.94.0.1 (tested on crossmipsel-gcc)':
    binutils-array-sects-compat.patch -> 1.1
    binutils-mips-relocs.patch -> 1.3

13 files changed:
binutils-absolute-gnu_debuglink-path.patch [new file with mode: 0644]
binutils-array-sects-compat.patch [new file with mode: 0644]
binutils-build-id.patch [new file with mode: 0644]
binutils-discarded.patch [new file with mode: 0644]
binutils-flex.patch [new file with mode: 0644]
binutils-gasp.patch [new file with mode: 0644]
binutils-info.patch [new file with mode: 0644]
binutils-libtool-m.patch [new file with mode: 0644]
binutils-libtool-relink.patch [new file with mode: 0644]
binutils-mips-relocs.patch [new file with mode: 0644]
binutils-pt_pax_flags.patch [new file with mode: 0644]
binutils-tooldir.patch [new file with mode: 0644]
binutils.spec [new file with mode: 0644]

diff --git a/binutils-absolute-gnu_debuglink-path.patch b/binutils-absolute-gnu_debuglink-path.patch
new file mode 100644 (file)
index 0000000..f4d3144
--- /dev/null
@@ -0,0 +1,50 @@
+--- binutils-2.18.50.0.2/bfd/opncls.c.orig     2007-10-03 17:52:57.000000000 +0200
++++ binutils-2.18.50.0.2/bfd/opncls.c  2007-10-22 22:30:03.233754376 +0200
+@@ -1241,6 +1241,10 @@
+       return NULL;
+     }
++  /* PLD: check the possible absolute debuginfo path (e.g. /tmp/.../usr/lib/debug/... */
++  if (separate_debug_file_exists (basename, crc32))
++    return basename;
++
+   for (dirlen = strlen (abfd->filename); dirlen > 0; dirlen--)
+     if (IS_DIR_SEPARATOR (abfd->filename[dirlen - 1]))
+       break;
+@@ -1345,6 +1349,15 @@
+   return find_separate_debug_file (abfd, dir);
+ }
++static char const*
++relaxed_filename (char const* filename)
++{
++  char const* relaxed = strstr (filename, "/usr/lib/debug/");
++  if ( relaxed != NULL )
++    return relaxed;
++  return filename;
++}
++
+ /*
+ FUNCTION
+       bfd_create_gnu_debuglink_section
+@@ -1376,8 +1389,7 @@
+       return NULL;
+     }
+-  /* Strip off any path components in filename.  */
+-  filename = lbasename (filename);
++  filename = relaxed_filename (filename);
+   sect = bfd_get_section_by_name (abfd, GNU_DEBUGLINK);
+   if (sect)
+@@ -1463,9 +1475,7 @@
+     crc32 = bfd_calc_gnu_debuglink_crc32 (crc32, buffer, count);
+   fclose (handle);
+-  /* Strip off any path components in filename,
+-     now that we no longer need them.  */
+-  filename = lbasename (filename);
++  filename = relaxed_filename (filename);
+   filelen = strlen (filename);
+   debuglink_size = filelen + 1;
diff --git a/binutils-array-sects-compat.patch b/binutils-array-sects-compat.patch
new file mode 100644 (file)
index 0000000..a45e14b
--- /dev/null
@@ -0,0 +1,20 @@
+--- binutils-2.13.90.0.16/ld/scripttempl/elf.sc.array-sects-compat~    2003-01-03 22:21:16.000000000 -0200
++++ binutils-2.13.90.0.16/ld/scripttempl/elf.sc        2003-01-03 22:27:42.000000000 -0200
+@@ -288,6 +288,17 @@
+   ${CREATE_SHLIB-${RELOCATING+. = ${DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
+   ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
++  /* For backward-compatibility with tools that don't support the
++     *_array_* sections below, our glibc's crt files contain weak
++     definitions of symbols that they reference.  We don't want to use
++     them, though, unless they're strictly necessary, because they'd
++     bring us empty sections, unlike PROVIDE below, so we drop the
++     sections from the crt files here.  */
++  /DISCARD/ : {
++      */crti.o(.init_array .fini_array .preinit_array)
++      */crtn.o(.init_array .fini_array .preinit_array)
++  }
++
+   /* Ensure the __preinit_array_start label is properly aligned.  We
+      could instead move the label definition inside the section, but
+      the linker would then create the section even if it turns out to
diff --git a/binutils-build-id.patch b/binutils-build-id.patch
new file mode 100644 (file)
index 0000000..d035dc4
--- /dev/null
@@ -0,0 +1,22 @@
+--- ../binutils-2.19.50.0.1.orig/bfd/elfcode.h 2008-11-26 09:29:54.000000000 +0000
++++ ./bfd/elfcode.h    2008-11-26 12:01:37.000000000 +0000
+@@ -1170,6 +1170,19 @@ elf_checksum_contents (bfd *abfd,
+       if (i_shdr.contents)
+       (*process) (i_shdr.contents, i_shdr.sh_size, arg);
++      else
++      {
++        asection *sec;
++
++        sec = bfd_section_from_elf_index (abfd, count);
++        if (sec != NULL)
++          {
++            if (sec->contents == NULL)
++              bfd_malloc_and_get_section (abfd, sec, & sec->contents);
++            if (sec->contents != NULL)
++              (*process) (sec->contents, i_shdr.sh_size, arg);
++          }
++      }
+     }
+   return TRUE;
diff --git a/binutils-discarded.patch b/binutils-discarded.patch
new file mode 100644 (file)
index 0000000..9db9831
--- /dev/null
@@ -0,0 +1,11 @@
+--- binutils-2.16.91.0.3/bfd/elflink.c.orig    2005-08-22 21:27:41.000000000 +0200
++++ binutils-2.16.91.0.3/bfd/elflink.c 2005-09-04 11:19:23.781924920 +0200
+@@ -7089,7 +7089,7 @@
+                     BFD_ASSERT (r_symndx != 0);
+                     if (action & COMPLAIN)
+                       (*finfo->info->callbacks->einfo)
+-                        (_("%X`%s' referenced in section `%A' of %B: "
++                        (_("`%s' referenced in section `%A' of %B: "
+                            "defined in discarded section `%A' of %B\n"),
+                          sym_name, o, input_bfd, sec, sec->owner);
diff --git a/binutils-flex.patch b/binutils-flex.patch
new file mode 100644 (file)
index 0000000..7f65fbb
--- /dev/null
@@ -0,0 +1,14 @@
+--- binutils-2.16.90.0.1/binutils/arlex.l.orig 2005-04-09 21:03:01.000000000 +0200
++++ binutils-2.16.90.0.1/binutils/arlex.l      2005-04-12 22:52:08.392751800 +0200
+@@ -1,3 +1,4 @@
++%option nounput
+ %{
+ /* arlex.l - Strange script language lexer */
+--- binutils-2.16.90.0.1/ld/ldlex.l.orig       2005-04-09 21:03:21.000000000 +0200
++++ binutils-2.16.90.0.1/ld/ldlex.l    2005-04-12 23:13:13.722392456 +0200
+@@ -1,3 +1,4 @@
++%option nounput
+ %{
+ /* Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
diff --git a/binutils-gasp.patch b/binutils-gasp.patch
new file mode 100644 (file)
index 0000000..a4a84c4
--- /dev/null
@@ -0,0 +1,5706 @@
+--- binutils-2.17.50.0.13/gas/Makefile.am.orig 2007-03-16 16:48:29.000000000 +0100
++++ binutils-2.17.50.0.13/gas/Makefile.am      2007-03-16 20:26:20.200480821 +0100
+@@ -199,7 +199,7 @@
+       symbols.c \
+       write.c
+-CFILES = $(GAS_CFILES) itbl-ops.c cgen.c
++CFILES = $(GAS_CFILES) gasp.c itbl-ops.c cgen.c
+ HFILES = \
+       as.h \
+@@ -455,7 +455,8 @@
+ # Note: GASP is now deprecated and has been removed.  It is still
+ # available in the CVS archive or older binutils releases if it is needed.
+-noinst_PROGRAMS = as-new
++# ...and it is needed for few packages in distribution.
++noinst_PROGRAMS = as-new gasp-new
+ noinst_SCRIPTS = $(GDBINIT)
+ EXTRA_SCRIPTS = .gdbinit
+@@ -517,6 +518,10 @@
+       as.h asintl.h bignum.h bit_fix.h config.h emul.h expr.h flonum.h \
+       frags.h hash.h listing.h obj.h read.h symbols.h tc.h write.h
++gasp_new_SOURCES = gasp.c macro.c sb.c hash.c
++gasp_new_LDADD = ../libiberty/libiberty.a $(INTLLIBS)
++gasp_new_DEPENDENCIES = ../libiberty/libiberty.a $(INTLDEPS)
++
+ EXPECT = expect
+ RUNTEST = runtest
+ RUNTESTFLAGS=
+@@ -682,6 +682,9 @@
+ itbl-lex.o: itbl-lex.c itbl-parse.h $(srcdir)/itbl-lex.h
+       $(COMPILE) -c $< $(NO_WERROR)
++gasp.o:gasp.c $(INCDIR)/getopt.h $(INCDIR)/safe-ctype.h \
++      sb.h macro.h $(INCDIR)/xregex.h $(INCDIR)/xregex2.h
++
+ # Disable -Werror, if it has been enabled, since old versions of bison/
+ # yacc will produce working code which contain compile time warnings.
+ itbl-parse.o: itbl-parse.c itbl-parse.h $(srcdir)/itbl-ops.h $(srcdir)/itbl-lex.h
+--- binutils-2.17.50.0.13/gas/doc/Makefile.am.orig     2007-03-16 16:48:29.000000000 +0100
++++ binutils-2.17.50.0.13/gas/doc/Makefile.am  2007-03-16 20:27:05.810679469 +0100
+@@ -15,7 +15,7 @@
+ man_MANS = as.1
+-info_TEXINFOS = as.texinfo 
++info_TEXINFOS = as.texinfo gasp.texinfo
+ as_TEXINFOS = asconfig.texi $(CPU_DOCS)
+ AM_MAKEINFOFLAGS = -I "$(srcdir)" -I "$(top_srcdir)/../libiberty" \
+--- binutils-2.16.91.0.2.org/gas/doc/gasp.texinfo      1970-01-01 00:00:00.000000000 +0000
++++ binutils-2.16.91.0.2/gas/doc/gasp.texinfo  2005-07-21 18:31:04.000000000 +0000
+@@ -0,0 +1,1456 @@
++\input texinfo               @c             -*- Texinfo -*-
++@setfilename gasp.info
++@c
++@c This file documents the assembly preprocessor "GASP"
++@c
++@c Copyright 1994, 1995, 2000, 2002 Free Software Foundation, Inc.
++@c
++@c    Permission is granted to copy, distribute and/or modify this document
++@c    under the terms of the GNU Free Documentation License, Version 1.1
++@c    or any later version published by the Free Software Foundation;
++@c    with no Invariant Sections, with no Front-Cover Texts, and with no
++@c    Back-Cover Texts.  A copy of the license is included in the
++@c    section entitled "GNU Free Documentation License".
++
++@ifinfo
++@format
++START-INFO-DIR-ENTRY
++* gasp: (gasp).                     The GNU Assembler Preprocessor
++END-INFO-DIR-ENTRY
++@end format
++@end ifinfo
++
++@syncodeindex ky cp
++@syncodeindex fn cp
++
++@finalout
++@setchapternewpage odd
++@settitle GASP
++@titlepage
++@c FIXME boring title
++@title GASP, an assembly preprocessor
++@subtitle for GASP version 1
++@sp 1
++@subtitle March 1994
++@author Roland Pesch
++@page
++
++@tex
++{\parskip=0pt \hfill Cygnus Support\par
++}
++@end tex
++
++@vskip 0pt plus 1filll
++Copyright @copyright{} 1994, 1995, 2000, 2002 Free Software Foundation, Inc.
++
++      Permission is granted to copy, distribute and/or modify this document
++      under the terms of the GNU Free Documentation License, Version 1.1
++      or any later version published by the Free Software Foundation;
++      with no Invariant Sections, with no Front-Cover Texts, and with no
++      Back-Cover Texts.  A copy of the license is included in the
++      section entitled "GNU Free Documentation License".
++
++@end titlepage
++
++@ifinfo
++Copyright @copyright{} 1994, 1995, 2000, 2002 Free Software Foundation, Inc.
++
++@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, distribute and/or modify this document
++      under the terms of the GNU Free Documentation License, Version 1.1
++      or any later version published by the Free Software Foundation;
++      with no Invariant Sections, with no Front-Cover Texts, and with no
++      Back-Cover Texts.  A copy of the license is included in the
++      section entitled "GNU Free Documentation License".
++
++
++@node Top
++@top GASP
++
++GASP is a preprocessor for assembly programs.
++
++This file describes version 1 of GASP.
++
++Steve Chamberlain wrote GASP; Roland Pesch wrote this manual.
++
++@menu
++* Overview::                    What is GASP?
++* Invoking GASP::               Command line options.
++* Commands::                    Preprocessor commands.
++* GNU Free Documentation License::  GNU Free Documentation License
++* Index::                       Index.
++@end menu
++@end ifinfo
++
++@node Overview
++@chapter What is GASP?
++
++The primary purpose of the @sc{gnu} assembler is to assemble the output of
++other programs---notably compilers.  When you have to hand-code
++specialized routines in assembly, that means the @sc{gnu} assembler is
++an unfriendly processor: it has no directives for macros, conditionals,
++or many other conveniences that you might expect.
++
++In some cases you can simply use the C preprocessor, or a generalized
++preprocessor like @sc{m4}; but this can be awkward, since none of these
++things are designed with assembly in mind.
++
++@sc{gasp} fills this need.  It is expressly designed to provide the
++facilities you need with hand-coded assembly code.  Implementing it as a
++preprocessor, rather than part of the assembler, allows the maximum
++flexibility: you can use it with hand-coded assembly, without paying a
++penalty of added complexity in the assembler you use for compiler
++output.
++
++@emph{Note} The use of @sc{gasp} has now been deprecated.  Anything
++that it could do can now be done by the macro facilities built into
++@sc{gas} itself.  At some point in the future the @sc{gasp} sources will
++be removed entirely from the binutils distribution.
++
++Here is a small example to give the flavor of @sc{gasp}.  This input to
++@sc{gasp}
++
++@cartouche
++@example
++        .MACRO  saveregs from=8 to=14
++count   .ASSIGNA \from
++        ! save r\from..r\to
++        .AWHILE  \&count LE \to
++        mov     r\&count,@@-sp
++count   .ASSIGNA  \&count + 1
++        .AENDW
++        .ENDM
++
++        saveregs from=12
++
++bar:    mov     #H'dead+10,r0
++foo     .SDATAC "hello"<10>
++        .END
++@end example
++@end cartouche
++
++@noindent
++generates this assembly program:
++
++@cartouche
++@example
++        ! save r12..r14
++        mov     r12,@@-sp
++        mov     r13,@@-sp
++        mov     r14,@@-sp
++
++bar:    mov     #57005+10,r0
++foo:    .byte   6,104,101,108,108,111,10
++@end example
++@end cartouche
++
++@node Invoking GASP
++@chapter Command Line Options
++
++@c FIXME!  Or is there a simpler way, calling from GAS option?
++The simplest way to use @sc{gasp} is to run it as a filter and assemble
++its output.  In Unix and its ilk, you can do this, for example:
++
++@c FIXME! GASP filename suffix convention?
++@example
++$ gasp prog.asm | as -o prog.o
++@end example
++
++Naturally, there are also a few command-line options to allow you to
++request variations on this basic theme.  Here is the full set of
++possibilities for the @sc{gasp} command line.
++
++@example
++gasp  [ -a | --alternate ]
++      [ -c @var{char} | --commentchar @var{char} ]
++      [ -d | --debug ]  [ -h | --help ] [ -M | --mri ]
++      [ -o @var{outfile} | --output @var{outfile} ]
++      [ -p | --print ]  [ -s | --copysource ]
++      [ -u | --unreasonable ]  [ -v | --version ]
++      @var{infile} @dots{}
++@end example
++
++@ftable @code
++@item @var{infile} @dots{}
++@c FIXME! Why not stdin as default infile?
++The input file names.  You must specify at least one input file; if you
++specify more, @sc{gasp} preprocesses them all, concatenating the output
++in the order you list the @var{infile} arguments.
++
++Mark the end of each input file with the preprocessor command
++@code{.END}.  @xref{Other Commands,, Miscellaneous commands}.
++
++@item -a
++@itemx --alternate
++Use alternative macro syntax.  @xref{Alternate,, Alternate macro
++syntax}, for a discussion of how this syntax differs from the default
++@sc{gasp} syntax.
++
++@cindex comment character, changing
++@cindex semicolon, as comment
++@cindex exclamation mark, as comment
++@cindex shriek, as comment
++@cindex bang, as comment
++@cindex @code{!} default comment char
++@cindex @code{;} as comment char
++@item -c '@var{char}'
++@itemx --commentchar '@var{char}'
++Use @var{char} as the comment character.  The default comment character
++is @samp{!}.  For example, to use a semicolon as the comment character,
++specify @w{@samp{-c ';'}} on the @sc{gasp} command line.  Since
++assembler command characters often have special significance to command
++shells, it is a good idea to quote or escape @var{char} when you specify
++a comment character.
++
++For the sake of simplicity, all examples in this manual use the default
++comment character @samp{!}.
++
++@item -d
++@itemx --debug
++Show debugging statistics.  In this version of @sc{gasp}, this option
++produces statistics about the string buffers that @sc{gasp} allocates
++internally.  For each defined buffersize @var{s}, @sc{gasp} shows the
++number of strings @var{n} that it allocated, with a line like this:
++
++@example
++strings size @var{s} : @var{n}
++@end example
++
++@noindent
++@sc{gasp} displays these statistics on the standard error stream, when
++done preprocessing.
++
++@item -h
++@itemx --help
++Display a summary of the @sc{gasp} command line options.
++
++@item -M
++@itemx --mri
++Use MRI compatibility mode.  Using this option causes @sc{gasp} to
++accept the syntax and pseudo-ops used by the Microtec Research
++@code{ASM68K} assembler.
++
++@item -o @var{outfile}
++@itemx --output @var{outfile}
++Write the output in a file called @var{outfile}.  If you do not use the
++@samp{-o} option, @sc{gasp} writes its output on the standard output
++stream.
++
++@item -p
++@itemx --print
++Print line numbers.  @sc{gasp} obeys this option @emph{only} if you also
++specify @samp{-s} to copy source lines to its output.  With @samp{-s
++-p}, @sc{gasp} displays the line number of each source line copied
++(immediately after the comment character at the beginning of the line).
++
++@item -s
++@itemx --copysource
++Copy the source lines to the output file.  Use this option
++to see the effect of each preprocessor line on the @sc{gasp} output.
++@sc{gasp} places a comment character (@samp{!} by default) at
++the beginning of each source line it copies, so that you can use this
++option and still assemble the result.
++
++@item -u
++@itemx --unreasonable
++Bypass ``unreasonable expansion'' limit.  Since you can define @sc{gasp}
++macros inside other macro definitions, the preprocessor normally
++includes a sanity check.  If your program requires more than 1,000
++nested expansions, @sc{gasp} normally exits with an error message.  Use
++this option to turn off this check, allowing unlimited nested
++expansions.
++
++@item -v
++@itemx --version
++Display the @sc{gasp} version number.
++@end ftable
++
++@node Commands
++@chapter Preprocessor Commands
++
++@sc{gasp} commands have a straightforward syntax that fits in well with
++assembly conventions.  In general, a command extends for a line, and may
++have up to three fields: an optional label, the command itself, and
++optional arguments to the command.  You can write commands in upper or
++lower case, though this manual shows them in upper case.  @xref{Syntax
++Details,, Details of the GASP syntax}, for more information.
++
++@menu
++* Conditionals::
++* Loops::
++* Variables::
++* Macros::
++* Data::
++* Listings::
++* Other Commands::
++* Syntax Details::
++* Alternate::
++@end menu
++
++@node Conditionals
++@section Conditional assembly
++
++The conditional-assembly directives allow you to include or exclude
++portions of an assembly depending on how a pair of expressions, or a
++pair of strings, compare.
++
++The overall structure of conditionals is familiar from many other
++contexts.  @code{.AIF} marks the start of a conditional, and precedes
++assembly for the case when the condition is true.   An optional
++@code{.AELSE} precedes assembly for the converse case, and an
++@code{.AENDI} marks the end of the condition.
++
++@c FIXME! Why doesn't -u turn off this check?
++You may nest conditionals up to a depth of 100; @sc{gasp} rejects
++nesting beyond that, because it may indicate a bug in your macro
++structure.
++
++@c FIXME! Why isn't there something like cpp's -D option?  Conditionals
++@c        would be much more useful if there were.
++Conditionals are primarily useful inside macro definitions, where you
++often need different effects depending on argument values.
++@xref{Macros,, Defining your own directives}, for details about defining
++macros.
++
++@ftable @code
++@item .AIF @var{expra} @var{cmp} @var{exprb}
++@itemx .AIF "@var{stra}" @var{cmp} "@var{strb}"
++
++The governing condition goes on the same line as the @code{.AIF}
++preprocessor command.  You may compare either two strings, or two
++expressions.
++
++When you compare strings, only two conditional @var{cmp} comparison
++operators are available: @samp{EQ} (true if @var{stra} and @var{strb}
++are identical), and @samp{NE} (the opposite).
++
++When you compare two expressions, @emph{both expressions must be
++absolute} (@pxref{Expressions,, Arithmetic expressions in GASP}).  You
++can use these @var{cmp} comparison operators with expressions:
++
++@ftable @code
++@item EQ
++Are @var{expra} and @var{exprb} equal?  (For strings, are @var{stra} and
++@var{strb} identical?)
++
++@item NE
++Are @var{expra} and @var{exprb} different?  (For strings, are @var{stra}
++and @var{strb} different?
++
++@item LT
++Is @var{expra} less than @var{exprb}?  (Not allowed for strings.)
++
++@item LE
++Is @var{expra} less than or equal to @var{exprb}?  (Not allowed for strings.)
++
++@item GT
++Is @var{expra} greater than @var{exprb}?  (Not allowed for strings.)
++
++@item GE
++Is @var{expra} greater than or equal to @var{exprb}?  (Not allowed for
++strings.)
++@end ftable
++
++@item .AELSE
++Marks the start of assembly code to be included if the condition fails.
++Optional, and only allowed within a conditional (between @code{.AIF} and
++@code{.AENDI}).
++
++@item .AENDI
++Marks the end of a conditional assembly.
++@end ftable
++
++@node Loops
++@section Repetitive sections of assembly
++
++Two preprocessor directives allow you to repeatedly issue copies of the
++same block of assembly code.
++
++@ftable @code
++@item .AREPEAT @var{aexp}
++@itemx .AENDR
++If you simply need to repeat the same block of assembly over and over a
++fixed number of times, sandwich one instance of the repeated block
++between @code{.AREPEAT} and @code{.AENDR}.  Specify the number of
++copies as @var{aexp} (which must be an absolute expression).  For
++example, this repeats two assembly statements three times in succession:
++
++@cartouche
++@example
++        .AREPEAT        3
++        rotcl   r2
++        div1    r0,r1
++        .AENDR
++@end example
++@end cartouche
++
++@item .AWHILE @var{expra} @var{cmp} @var{exprb}
++@itemx .AENDW
++@itemx .AWHILE @var{stra} @var{cmp} @var{strb}
++@itemx .AENDW
++To repeat a block of assembly depending on a conditional test, rather
++than repeating it for a specific number of times, use @code{.AWHILE}.
++@code{.AENDW} marks the end of the repeated block.  The conditional
++comparison works exactly the same way as for @code{.AIF}, with the same
++comparison operators (@pxref{Conditionals,, Conditional assembly}).
++
++Since the terms of the comparison must be absolute expression,
++@code{.AWHILE} is primarily useful within macros.  @xref{Macros,,
++Defining your own directives}.
++@end ftable
++
++@cindex loops, breaking out of
++@cindex breaking out of loops
++You can use the @code{.EXITM} preprocessor directive to break out of
++loops early (as well as to break out of macros).  @xref{Macros,,
++Defining your own directives}.
++
++@node Variables
++@section Preprocessor variables
++
++You can use variables in @sc{gasp} to represent strings, registers, or
++the results of expressions.
++
++You must distinguish two kinds of variables: 
++@enumerate
++@item
++Variables defined with @code{.EQU} or @code{.ASSIGN}.  To evaluate this
++kind of variable in your assembly output, simply mention its name.  For
++example, these two lines define and use a variable @samp{eg}:
++
++@cartouche
++@example
++eg     .EQU   FLIP-64
++       @dots{}
++       mov.l  eg,r0
++@end example
++@end cartouche
++
++@emph{Do not use} this kind of variable in conditional expressions or
++while loops; @sc{gasp} only evaluates these variables when writing
++assembly output.
++
++@item
++Variables for use during preprocessing.  You can define these
++with @code{.ASSIGNC} or @code{.ASSIGNA}.  To evaluate this
++kind of variable, write @samp{\&} before the variable name; for example,
++
++@cartouche
++@example
++opcit  .ASSIGNA  47
++       @dots{}
++       .AWHILE  \&opcit GT 0
++       @dots{}
++       .AENDW
++@end example
++@end cartouche
++
++@sc{gasp} treats macro arguments almost the same way, but to evaluate
++them you use the prefix @samp{\} rather than @samp{\&}.
++@xref{Macros,, Defining your own directives}.
++@end enumerate
++
++@ftable @code
++@item @var{pvar} .EQU @var{expr}
++@c FIXME!  Anything to beware of re GAS directive of same name?
++Assign preprocessor variable @var{pvar} the value of the expression
++@var{expr}.  There are no restrictions on redefinition; use @samp{.EQU}
++with the same @var{pvar} as often as you find it convenient.
++
++@item @var{pvar} .ASSIGN @var{expr}
++Almost the same as @code{.EQU}, save that you may not redefine
++@var{pvar} using @code{.ASSIGN} once it has a value.
++@c FIXME!!  Supposed to work this way, apparently, but on 9feb94 works
++@c          just like .EQU
++
++@item @var{pvar} .ASSIGNA @var{aexpr}
++Define a variable with a numeric value, for use during preprocessing.
++@var{aexpr} must be an absolute expression.  You can redefine variables
++with @code{.ASSIGNA} at any time.
++
++@item @var{pvar} .ASSIGNC "@var{str}"
++Define a variable with a string value, for use during preprocessing.
++You can redefine variables with @code{.ASSIGNC} at any time.
++
++@item @var{pvar} .REG (@var{register})
++Use @code{.REG} to define a variable that represents a register.  In
++particular, @var{register} is @emph{not evaluated} as an expression.
++You may use @code{.REG} at will to redefine register variables.
++@end ftable
++
++All these directives accept the variable name in the ``label'' position,
++that is at the left margin.  You may specify a colon after the variable
++name if you wish; the first example above could have started @samp{eg:}
++with the same effect.
++
++@c pagebreak makes for better aesthetics---ensures macro and expansion together
++@page
++@node Macros
++@section Defining your own directives
++
++The commands @code{.MACRO} and @code{.ENDM} allow you to define macros
++that generate assembly output.  You can use these macros with a syntax
++similar to built-in @sc{gasp} or assembler directives.  For example,
++this definition specifies a macro @code{SUM} that adds together a range of
++consecutive registers:
++
++@cartouche
++@example
++        .MACRO  SUM FROM=0, TO=9
++        ! \FROM \TO
++        mov     r\FROM,r10
++COUNT   .ASSIGNA        \FROM+1
++        .AWHILE \&COUNT LE \TO
++        add     r\&COUNT,r10
++COUNT   .ASSIGNA        \&COUNT+1
++        .AENDW
++        .ENDM
++@end example
++@end cartouche
++
++@noindent
++With that definition, @samp{SUM 0,5} generates this assembly output:
++
++@cartouche
++@example
++        ! 0 5
++        mov     r0,r10
++        add     r1,r10
++        add     r2,r10
++        add     r3,r10
++        add     r4,r10
++        add     r5,r10
++@end example
++@end cartouche
++
++@ftable @code
++@item .MACRO @var{macname}
++@itemx .MACRO @var{macname} @var{macargs} @dots{}
++Begin the definition of a macro called @var{macname}.  If your macro
++definition requires arguments, specify their names after the macro name,
++separated by commas or spaces.  You can supply a default value for any
++macro argument by following the name with @samp{=@var{deflt}}.  For
++example, these are all valid @code{.MACRO} statements:
++
++@table @code
++@item .MACRO COMM
++Begin the definition of a macro called @code{COMM}, which takes no
++arguments.
++
++@item .MACRO PLUS1 P, P1
++@itemx .MACRO PLUS1 P P1
++Either statement begins the definition of a macro called @code{PLUS1},
++which takes two arguments; within the macro definition, write
++@samp{\P} or @samp{\P1} to evaluate the arguments.
++
++@item .MACRO RESERVE_STR P1=0 P2
++Begin the definition of a macro called @code{RESERVE_STR}, with two
++arguments.  The first argument has a default value, but not the second.
++After the definition is complete, you can call the macro either as
++@samp{RESERVE_STR @var{a},@var{b}} (with @samp{\P1} evaluating to
++@var{a} and @samp{\P2} evaluating to @var{b}), or as @samp{RESERVE_STR
++,@var{b}} (with @samp{\P1} evaluating as the default, in this case
++@samp{0}, and @samp{\P2} evaluating to @var{b}).
++@end table
++
++When you call a macro, you can specify the argument values either by
++position, or by keyword.  For example, @samp{SUM 9,17} is equivalent to
++@samp{SUM TO=17, FROM=9}.  Macro arguments are preprocessor variables
++similar to the variables you define with @samp{.ASSIGNA} or
++@samp{.ASSIGNC}; in particular, you can use them in conditionals or for
++loop control.  (The only difference is the prefix you write to evaluate
++the variable: for a macro argument, write @samp{\@var{argname}}, but for
++a preprocessor variable, write @samp{\&@var{varname}}.)
++
++@item @var{name} .MACRO
++@itemx @var{name} .MACRO ( @var{macargs} @dots{} )
++@c FIXME check: I think no error _and_ no args recognized if I use form
++@c       NAME  .MACRO   ARG ARG
++An alternative form of introducing a macro definition: specify the macro
++name in the label position, and the arguments (if any) between
++parentheses after the name.  Defaulting rules and usage work the same
++way as for the other macro definition syntax.
++
++@item .ENDM
++Mark the end of a macro definition.
++
++@item .EXITM
++Exit early from the current macro definition, @code{.AREPEAT} loop, or
++@code{.AWHILE} loop.
++
++@cindex number of macros executed
++@cindex macros, count executed
++@item \@@
++@sc{gasp} maintains a counter of how many macros it has
++executed in this pseudo-variable; you can copy that number to your
++output with @samp{\@@}, but @emph{only within a macro definition}.
++
++@item LOCAL @var{name} [ , @dots{} ]
++@emph{Warning: @code{LOCAL} is only available if you select ``alternate
++macro syntax'' with @samp{-a} or @samp{--alternate}.}  @xref{Alternate,,
++Alternate macro syntax}.
++
++Generate a string replacement for each of the @var{name} arguments, and
++replace any instances of @var{name} in each macro expansion.  The
++replacement string is unique in the assembly, and different for each
++separate macro expansion.  @code{LOCAL} allows you to write macros that
++define symbols, without fear of conflict between separate macro expansions.
++@end ftable
++
++@node Data
++@section Data output
++
++In assembly code, you often need to specify working areas of memory;
++depending on the application, you may want to initialize such memory or
++not.  @sc{gasp} provides preprocessor directives to help you avoid
++repetitive coding for both purposes.
++
++You can use labels as usual to mark the data areas.
++
++@menu
++* Initialized::
++* Uninitialized::
++@end menu
++
++@node Initialized
++@subsection Initialized data
++
++These are the @sc{gasp} directives for initialized data, and the standard
++@sc{gnu} assembler directives they expand to:
++
++@ftable @code
++@item .DATA @var{expr}, @var{expr}, @dots{}
++@itemx .DATA.B @var{expr}, @var{expr}, @dots{}
++@itemx .DATA.W @var{expr}, @var{expr}, @dots{}
++@itemx .DATA.L @var{expr}, @var{expr}, @dots{}
++Evaluate arithmetic expressions @var{expr}, and emit the corresponding
++@code{as} directive (labelled with @var{lab}).  The unqualified
++@code{.DATA} emits @samp{.long}; @code{.DATA.B} emits @samp{.byte};
++@code{.DATA.W} emits @samp{.short}; and @code{.DATA.L} emits
++@samp{.long}.
++
++For example, @samp{foo .DATA 1,2,3} emits @samp{foo: .long 1,2,3}.
++
++@item .DATAB @var{repeat}, @var{expr}
++@itemx .DATAB.B @var{repeat}, @var{expr}
++@itemx .DATAB.W @var{repeat}, @var{expr}
++@itemx .DATAB.L @var{repeat}, @var{expr}
++@c FIXME! Looks like gasp accepts and ignores args after 2nd.
++Make @code{as} emit @var{repeat} copies of the value of the expression
++@var{expr} (using the @code{as} directive @code{.fill}).
++@samp{.DATAB.B} repeats one-byte values; @samp{.DATAB.W} repeats
++two-byte values; and @samp{.DATAB.L} repeats four-byte values.
++@samp{.DATAB} without a suffix repeats four-byte values, just like
++@samp{.DATAB.L}.
++
++@c FIXME! Allowing zero might be useful for edge conditions in macros.
++@var{repeat} must be an absolute expression with a positive value.
++
++@item .SDATA "@var{str}" @dots{}
++String data.  Emits a concatenation of bytes, precisely as you specify
++them (in particular, @emph{nothing is added to mark the end} of the
++string).  @xref{Constants,, String and numeric constants}, for details
++about how to write strings.  @code{.SDATA} concatenates multiple
++arguments, making it easy to switch between string representations.  You
++can use commas to separate the individual arguments for clarity, if you
++choose.
++
++@item .SDATAB @var{repeat}, "@var{str}" @dots{}
++Repeated string data.  The first argument specifies how many copies of
++the string to emit; the remaining arguments specify the string, in the
++same way as the arguments to @code{.SDATA}.
++
++@item .SDATAZ "@var{str}" @dots{}
++Zero-terminated string data.  Just like @code{.SDATA}, except that
++@code{.SDATAZ} writes a zero byte at the end of the string.
++
++@item .SDATAC "@var{str}" @dots{}
++Count-prefixed string data.  Just like @code{.SDATA}, except that
++@sc{gasp} precedes the string with a leading one-byte count.  For
++example, @samp{.SDATAC "HI"} generates @samp{.byte 2,72,73}.  Since the
++count field is only one byte, you can only use @code{.SDATAC} for
++strings less than 256 bytes in length.
++@end ftable
++
++@node Uninitialized
++@subsection Uninitialized data
++
++@c FIXME!  .space different on some platforms, notably HPPA.  Config?
++Use the @code{.RES}, @code{.SRES}, @code{.SRESC}, and @code{.SRESZ}
++directives to reserve memory and leave it uninitialized.  @sc{gasp}
++resolves these directives to appropriate calls of the @sc{gnu}
++@code{as} @code{.space} directive.
++
++@ftable @code
++@item .RES @var{count}
++@itemx .RES.B @var{count}
++@itemx .RES.W @var{count}
++@itemx .RES.L @var{count}
++Reserve room for @var{count} uninitialized elements of data.  The
++suffix specifies the size of each element: @code{.RES.B} reserves
++@var{count} bytes, @code{.RES.W} reserves @var{count} pairs of bytes,
++and @code{.RES.L} reserves @var{count} quartets.  @code{.RES} without a
++suffix is equivalent to @code{.RES.L}.
++
++@item .SRES @var{count}
++@itemx .SRES.B @var{count}
++@itemx .SRES.W @var{count}
++@itemx .SRES.L @var{count}
++@c FIXME!  This is boring.  Shouldn't it at least have a different
++@c         default size?  (e.g. the "S" suggests "string", for which .B
++@c         would be more appropriate)
++@code{.SRES} is a synonym for @samp{.RES}.
++
++@item .SRESC @var{count}
++@itemx .SRESC.B @var{count}
++@itemx .SRESC.W @var{count}
++@itemx .SRESC.L @var{count}
++Like @code{.SRES}, but reserves space for @code{@var{count}+1} elements.
++
++@item .SRESZ @var{count}
++@itemx .SRESZ.B @var{count}
++@itemx .SRESZ.W @var{count}
++@itemx .SRESZ.L @var{count}
++Like @code{.SRES}, but reserves space for @code{@var{count}+1} elements.
++@end ftable
++
++@node Listings
++@section Assembly listing control
++
++The @sc{gasp} listing-control directives correspond to
++related @sc{gnu} @code{as} directives.
++
++@ftable @code
++@item .PRINT LIST
++@itemx .PRINT NOLIST
++Print control.  This directive emits the @sc{gnu} @code{as} directive
++@code{.list} or @code{.nolist}, according to its argument.  @xref{List,,
++@code{.list}, as.info, Using as}, for details on how these directives
++interact.
++
++@item .FORM LIN=@var{ln}
++@itemx .FORM COL=@var{cols}
++@itemx .FORM LIN=@var{ln} COL=@var{cols}
++Specify the page size for assembly listings: @var{ln} represents the
++number of lines, and @var{cols} the number of columns.  You may specify
++either page dimension independently, or both together.  If you do not
++specify the number of lines, @sc{gasp} assumes 60 lines; if you do not
++specify the number of columns, @sc{gasp} assumes 132 columns.
++(Any values you may have specified in previous instances of @code{.FORM}
++do @emph{not} carry over as defaults.)  Emits the @code{.psize}
++assembler directive.
++
++@item .HEADING @var{string}
++Specify @var{string} as the title of your assembly listings.  Emits
++@samp{.title "@var{string}"}.
++
++@item .PAGE
++Force a new page in assembly listings.  Emits @samp{.eject}.
++@end ftable
++
++@node Other Commands
++@section Miscellaneous commands
++
++@ftable @code
++@item .ALTERNATE
++Use the alternate macro syntax henceforth in the assembly.
++@xref{Alternate,, Alternate macro syntax}.
++
++@item .ORG
++@c FIXME!  This is very strange, since _GAS_ understands .org
++This command is recognized, but not yet implemented.  @sc{gasp}
++generates an error message for programs that use @code{.ORG}.
++
++@item .RADIX @var{s}
++@c FIXME no test cases in testsuite/gasp
++@sc{gasp} understands numbers in any of base two, eight, ten, or
++sixteen.  You can encode the base explicitly in any numeric constant
++(@pxref{Constants,, String and numeric constants}).  If you write
++numbers without an explicit indication of the base, the most recent
++@samp{.RADIX @var{s}} command determines how they are interpreted.
++@var{s} is a single letter, one of the following:
++
++@table @code
++@item .RADIX B
++Base 2.
++
++@item .RADIX Q
++Base 8.
++
++@item .RADIX D
++Base 10.  This is the original default radix.
++
++@item .RADIX H
++Base 16.
++@end table
++
++You may specify the argument @var{s} in lower case (any of @samp{bqdh})
++with the same effects.
++
++@item .EXPORT @var{name}
++@itemx .GLOBAL @var{name}
++@c FIXME! No test cases in testsuite/gasp
++Declare @var{name} global (emits @samp{.global @var{name}}).  The two
++directives are synonymous.
++
++@item .PROGRAM
++No effect: @sc{gasp} accepts this directive, and silently ignores it.
++
++@item .END
++Mark end of each preprocessor file.  @sc{gasp} issues a warning if it
++reaches end of file without seeing this command.
++
++@item .INCLUDE "@var{str}"
++Preprocess the file named by @var{str}, as if its contents appeared
++where the @code{.INCLUDE} directive does.  @sc{gasp} imposes a maximum
++limit of 30 stacked include files, as a sanity check.
++@c FIXME!  Why is include depth not affected by -u?
++
++@item .ALIGN @var{size}
++@c FIXME! Why is this not utterly pointless?
++Evaluate the absolute expression @var{size}, and emit the assembly
++instruction @samp{.align @var{size}} using the result.
++@end ftable
++
++@node Syntax Details
++@section Details of the GASP syntax
++
++Since @sc{gasp} is meant to work with assembly code, its statement
++syntax has no surprises for the assembly programmer.
++
++@cindex whitespace
++@emph{Whitespace} (blanks or tabs; @emph{not} newline) is partially
++significant, in that it delimits up to three fields in a line.  The
++amount of whitespace does not matter; you may line up fields in separate
++lines if you wish, but @sc{gasp} does not require that.
++
++@cindex fields of @sc{gasp} source line
++@cindex label field
++The @emph{first field}, an optional @dfn{label}, must be flush left in a
++line (with no leading whitespace) if it appears at all.  You may use a
++colon after the label if you wish; @sc{gasp} neither requires the colon
++nor objects to it (but will not include it as part of the label name).
++
++@cindex directive field
++The @emph{second field}, which must appear after some whitespace,
++contains a @sc{gasp} or assembly @dfn{directive}.
++
++@cindex argument fields
++Any @emph{further fields} on a line are @dfn{arguments} to the
++directive; you can separate them from one another using either commas or
++whitespace.
++
++@menu
++* Markers::
++* Constants::
++* Symbols::
++* Expressions::
++* String Builtins::
++@end menu
++
++@node Markers
++@subsection Special syntactic markers
++
++@sc{gasp} recognizes a few special markers: to delimit comments, to
++continue a statement on the next line, to separate symbols from other
++characters, and to copy text to the output literally.  (One other
++special marker, @samp{\@@}, works only within macro definitions;
++@pxref{Macros,, Defining your own directives}.)
++
++@cindex comments
++The trailing part of any @sc{gasp} source line may be a @dfn{comment}.
++A comment begins with the first unquoted comment character (@samp{!} by
++default), or an escaped or doubled comment character (@samp{\!} or
++@samp{!!} by default), and extends to the end of a line.  You can
++specify what comment character to use with the @samp{-c} option
++(@pxref{Invoking GASP,, Command Line Options}).  The two kinds of
++comment markers lead to slightly different treatment:
++
++@table @code
++@item !
++A single, un-escaped comment character generates an assembly comment in
++the @sc{gasp} output.  @sc{gasp} evaluates any preprocessor variables
++(macro arguments, or variables defined with @code{.ASSIGNA} or
++@code{.ASSIGNC}) present.  For example, a macro that begins like this
++
++@example
++        .MACRO  SUM FROM=0, TO=9
++        ! \FROM \TO
++@end example
++
++@noindent
++issues as the first line of output a comment that records the
++values you used to call the macro.
++
++@c comments, preprocessor-only
++@c preprocessor-only comments
++@c GASP-only comments
++@item \!
++@itemx !!
++Either an escaped comment character, or a double comment character,
++marks a @sc{gasp} source comment.  @sc{gasp} does not copy such comments
++to the assembly output.
++@end table
++
++@cindex continuation character
++@kindex +
++To @emph{continue a statement} on the next line of the file, begin the
++second line with the character @samp{+}.
++
++@cindex literal copy to output
++@cindex copying literally to output
++@cindex preprocessing, avoiding
++@cindex avoiding preprocessing
++Occasionally you may want to prevent @sc{gasp} from preprocessing some
++particular bit of text.  To @emph{copy literally} from the @sc{gasp}
++source to its output, place @samp{\(} before the string to copy, and
++@samp{)} at the end.  For example, write @samp{\(\!)} if you need the
++characters @samp{\!} in your assembly output.
++
++@cindex symbol separator
++@cindex text, separating from symbols
++@cindex symbols, separating from text
++To @emph{separate a preprocessor variable} from text to appear
++immediately after its value, write a single quote (@code{'}).  For
++example, @samp{.SDATA "\P'1"} writes a string built by concatenating the
++value of @code{P} and the digit @samp{1}.  (You cannot achieve this by
++writing just @samp{\P1}, since @samp{P1} is itself a valid name for a
++preprocessor variable.)
++
++@node Constants
++@subsection String and numeric constants
++
++There are two ways of writing @dfn{string constants} in @sc{gasp}: as
++literal text, and by numeric byte value.  Specify a string literal
++between double quotes (@code{"@var{str}"}).  Specify an individual
++numeric byte value as an absolute expression between angle brackets
++(@code{<@var{expr}>}.  Directives that output strings allow you to
++specify any number of either kind of value, in whatever order is
++convenient, and concatenate the result.  (Alternate syntax mode
++introduces a number of alternative string notations; @pxref{Alternate,,
++Alternate macro syntax}.)
++
++@c Details of numeric notation, e.g. base prefixes
++You can write @dfn{numeric constants} either in a specific base, or in
++whatever base is currently selected (either 10, or selected by the most
++recent @code{.RADIX}).
++
++To write a number in a @emph{specific base}, use the pattern
++@code{@var{s}'@var{ddd}}: a base specifier character @var{s}, followed
++by a single quote followed by digits @var{ddd}.  The base specifier
++character matches those you can specify with @code{.RADIX}: @samp{B} for
++base 2, @samp{Q} for base 8, @samp{D} for base 10, and @samp{H} for base
++16.  (You can write this character in lower case if you prefer.)
++
++You can write floating point constants using the same syntax recognised
++by GAS @ref{Flonums,,Flonums,as,The GNU Assembler.}.  A constraint is
++that these constants will be interpreted as decimal values irrespective
++of the currently selected base.
++
++@c FIXME! What are rules for recognizing number in deflt base?  Whatever
++@c        is left over after parsing other things??
++
++@node Symbols
++@subsection Symbols
++
++@sc{gasp} recognizes symbol names that start with any alphabetic character,
++@samp{_}, or @samp{$}, and continue with any of the same characters or
++with digits.  Label names follow the same rules.
++
++@node Expressions
++@subsection Arithmetic expressions in GASP
++
++@cindex absolute expressions
++@cindex relocatable expressions
++There are two kinds of expressions, depending on their result:
++@dfn{absolute} expressions, which resolve to a constant (that is, they
++do not involve any values unknown to @sc{gasp}), and @dfn{relocatable}
++expressions, which must reduce to the form
++
++@example
++@var{addsym}+@var{const}-@var{subsym}
++@end example
++
++@noindent
++where @var{addsym} and @var{subsym} are assembly symbols of unknown
++value, and @var{const} is a constant.
++
++Arithmetic for @sc{gasp} expressions follows very similar rules to C.
++You can use parentheses to change precedence; otherwise, arithmetic
++primitives have decreasing precedence in the order of the following
++list.
++
++@enumerate
++@item
++Single-argument @code{+} (identity), @code{-} (arithmetic opposite), or
++@code{~} (bitwise negation).  @emph{The argument must be an absolute
++expression.}
++
++@item
++@code{*} (multiplication) and @code{/} (division).  @emph{Both arguments
++must be absolute expressions.}
++
++@item
++@code{+} (addition) and @code{-} (subtraction).  @emph{At least one argument
++must be absolute.}
++@c FIXME!  Actually, subtraction doesn't check for this.
++
++@item
++@code{&} (bitwise and).  @emph{Both arguments must be absolute.}
++
++@item
++@c FIXME!  I agree ~ is a better notation than ^ for xor, but is the
++@c         improvement worth differing from C?
++@code{|} (bitwise or) and @code{~} (bitwise exclusive or; @code{^} in
++C).  @emph{Both arguments must be absolute.}
++@end enumerate
++
++@node String Builtins
++@subsection String primitives
++
++You can use these primitives to manipulate strings (in the argument
++field of @sc{gasp} statements):
++
++@ftable @code
++@item .LEN("@var{str}")
++Calculate the length of string @code{"@var{str}"}, as an absolute
++expression.  For example, @samp{.RES.B .LEN("sample")} reserves six
++bytes of memory.
++
++@item .INSTR("@var{string}", "@var{seg}", @var{ix})
++Search for the first occurrence of @var{seg} after position @var{ix} of
++@var{string}.  For example, @samp{.INSTR("ABCDEFG", "CDE", 0)} evaluates
++to the absolute result @code{2}.
++
++The result is @code{-1} if @var{seg} does not occur in @var{string}
++after position @var{ix}.
++
++@item .SUBSTR("@var{string}",@var{start},@var{len})
++The substring of @var{string} beginning at byte number @var{start} and
++extending for @var{len} bytes.
++@end ftable
++
++@node Alternate
++@section Alternate macro syntax
++
++If you specify @samp{-a} or @samp{--alternate} on the @sc{gasp} command
++line, the preprocessor uses somewhat different syntax.  This syntax is
++reminiscent of the syntax of Phar Lap macro assembler, but it
++is @emph{not} meant to be a full emulation of Phar Lap or similar
++assemblers.  In particular, @sc{gasp} does not support directives such
++as @code{DB} and @code{IRP}, even in alternate syntax mode.
++
++In particular, @samp{-a} (or @samp{--alternate}) elicits these
++differences:
++
++@table @emph
++@item Preprocessor directives
++You can use @sc{gasp} preprocessor directives without a leading @samp{.}
++dot.  For example, you can write @samp{SDATA} with the same effect as
++@samp{.SDATA}.
++
++@item LOCAL
++One additional directive, @code{LOCAL}, is available.  @xref{Macros,,
++Defining your own directives}, for an explanation of how to use
++@code{LOCAL}.
++
++@need 2000
++@item String delimiters
++You can write strings delimited in these other ways besides
++@code{"@var{string}"}:
++
++@table @code
++@item '@var{string}'
++You can delimit strings with single-quote charaters.
++
++@item <@var{string}>
++You can delimit strings with matching angle brackets.
++@end table
++
++@item single-character string escape
++To include any single character literally in a string (even if the
++character would otherwise have some special meaning), you can prefix the
++character with @samp{!} (an exclamation mark).  For example, you can
++write @samp{<4.3 !> 5.4!!>} to get the literal text @samp{4.3 > 5.4!}.
++
++@item Expression results as strings
++You can write @samp{%@var{expr}} to evaluate the expression @var{expr}
++and use the result as a string.  
++@end table
++
++@node GNU Free Documentation License
++@chapter GNU Free Documentation License
++
++                GNU Free Documentation License
++                
++                   Version 1.1, March 2000
++
++ Copyright (C) 2000  Free Software Foundation, Inc.
++  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++     
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++
++0. PREAMBLE
++
++The purpose of this License is to make a manual, textbook, or other
++written document "free" in the sense of freedom: to assure everyone
++the effective freedom to copy and redistribute it, with or without
++modifying it, either commercially or noncommercially.  Secondarily,
++this License preserves for the author and publisher a way to get
++credit for their work, while not being considered responsible for
++modifications made by others.
++
++This License is a kind of "copyleft", which means that derivative
++works of the document must themselves be free in the same sense.  It
++complements the GNU General Public License, which is a copyleft
++license designed for free software.
++
++We have designed this License in order to use it for manuals for free
++software, because free software needs free documentation: a free
++program should come with manuals providing the same freedoms that the
++software does.  But this License is not limited to software manuals;
++it can be used for any textual work, regardless of subject matter or
++whether it is published as a printed book.  We recommend this License
++principally for works whose purpose is instruction or reference.
++
++
++1. APPLICABILITY AND DEFINITIONS
++
++This License applies to any manual or other work that contains a
++notice placed by the copyright holder saying it can be distributed
++under the terms of this License.  The "Document", below, refers to any
++such manual or work.  Any member of the public is a licensee, and is
++addressed as "you".
++
++A "Modified Version" of the Document means any work containing the
++Document or a portion of it, either copied verbatim, or with
++modifications and/or translated into another language.
++
++A "Secondary Section" is a named appendix or a front-matter section of
++the Document that deals exclusively with the relationship of the
++publishers or authors of the Document to the Document's overall subject
++(or to related matters) and contains nothing that could fall directly
++within that overall subject.  (For example, if the Document is in part a
++textbook of mathematics, a Secondary Section may not explain any
++mathematics.)  The relationship could be a matter of historical
++connection with the subject or with related matters, or of legal,
++commercial, philosophical, ethical or political position regarding
++them.
++
++The "Invariant Sections" are certain Secondary Sections whose titles
++are designated, as being those of Invariant Sections, in the notice
++that says that the Document is released under this License.
++
++The "Cover Texts" are certain short passages of text that are listed,
++as Front-Cover Texts or Back-Cover Texts, in the notice that says that
++the Document is released under this License.
++
++A "Transparent" copy of the Document means a machine-readable copy,
++represented in a format whose specification is available to the
++general public, whose contents can be viewed and edited directly and
++straightforwardly with generic text editors or (for images composed of
++pixels) generic paint programs or (for drawings) some widely available
++drawing editor, and that is suitable for input to text formatters or
++for automatic translation to a variety of formats suitable for input
++to text formatters.  A copy made in an otherwise Transparent file
++format whose markup has been designed to thwart or discourage
++subsequent modification by readers is not Transparent.  A copy that is
++not "Transparent" is called "Opaque".
++
++Examples of suitable formats for Transparent copies include plain
++ASCII without markup, Texinfo input format, LaTeX input format, SGML
++or XML using a publicly available DTD, and standard-conforming simple
++HTML designed for human modification.  Opaque formats include
++PostScript, PDF, proprietary formats that can be read and edited only
++by proprietary word processors, SGML or XML for which the DTD and/or
++processing tools are not generally available, and the
++machine-generated HTML produced by some word processors for output
++purposes only.
++
++The "Title Page" means, for a printed book, the title page itself,
++plus such following pages as are needed to hold, legibly, the material
++this License requires to appear in the title page.  For works in
++formats which do not have any title page as such, "Title Page" means
++the text near the most prominent appearance of the work's title,
++preceding the beginning of the body of the text.
++
++
++2. VERBATIM COPYING
++
++You may copy and distribute the Document in any medium, either
++commercially or noncommercially, provided that this License, the
++copyright notices, and the license notice saying this License applies
++to the Document are reproduced in all copies, and that you add no other
++conditions whatsoever to those of this License.  You may not use
++technical measures to obstruct or control the reading or further
++copying of the copies you make or distribute.  However, you may accept
++compensation in exchange for copies.  If you distribute a large enough
++number of copies you must also follow the conditions in section 3.
++
++You may also lend copies, under the same conditions stated above, and
++you may publicly display copies.
++
++
++3. COPYING IN QUANTITY
++
++If you publish printed copies of the Document numbering more than 100,
++and the Document's license notice requires Cover Texts, you must enclose
++the copies in covers that carry, clearly and legibly, all these Cover
++Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
++the back cover.  Both covers must also clearly and legibly identify
++you as the publisher of these copies.  The front cover must present
++the full title with all words of the title equally prominent and
++visible.  You may add other material on the covers in addition.
++Copying with changes limited to the covers, as long as they preserve
++the title of the Document and satisfy these conditions, can be treated
++as verbatim copying in other respects.
++
++If the required texts for either cover are too voluminous to fit
++legibly, you should put the first ones listed (as many as fit
++reasonably) on the actual cover, and continue the rest onto adjacent
++pages.
++
++If you publish or distribute Opaque copies of the Document numbering
++more than 100, you must either include a machine-readable Transparent
++copy along with each Opaque copy, or state in or with each Opaque copy
++a publicly-accessible computer-network location containing a complete
++Transparent copy of the Document, free of added material, which the
++general network-using public has access to download anonymously at no
++charge using public-standard network protocols.  If you use the latter
++option, you must take reasonably prudent steps, when you begin
++distribution of Opaque copies in quantity, to ensure that this
++Transparent copy will remain thus accessible at the stated location
++until at least one year after the last time you distribute an Opaque
++copy (directly or through your agents or retailers) of that edition to
++the public.
++
++It is requested, but not required, that you contact the authors of the
++Document well before redistributing any large number of copies, to give
++them a chance to provide you with an updated version of the Document.
++
++
++4. MODIFICATIONS
++
++You may copy and distribute a Modified Version of the Document under
++the conditions of sections 2 and 3 above, provided that you release
++the Modified Version under precisely this License, with the Modified
++Version filling the role of the Document, thus licensing distribution
++and modification of the Modified Version to whoever possesses a copy
++of it.  In addition, you must do these things in the Modified Version:
++
++A. Use in the Title Page (and on the covers, if any) a title distinct
++   from that of the Document, and from those of previous versions
++   (which should, if there were any, be listed in the History section
++   of the Document).  You may use the same title as a previous version
++   if the original publisher of that version gives permission.
++B. List on the Title Page, as authors, one or more persons or entities
++   responsible for authorship of the modifications in the Modified
++   Version, together with at least five of the principal authors of the
++   Document (all of its principal authors, if it has less than five).
++C. State on the Title page the name of the publisher of the
++   Modified Version, as the publisher.
++D. Preserve all the copyright notices of the Document.
++E. Add an appropriate copyright notice for your modifications
++   adjacent to the other copyright notices.
++F. Include, immediately after the copyright notices, a license notice
++   giving the public permission to use the Modified Version under the
++   terms of this License, in the form shown in the Addendum below.
++G. Preserve in that license notice the full lists of Invariant Sections
++   and required Cover Texts given in the Document's license notice.
++H. Include an unaltered copy of this License.
++I. Preserve the section entitled "History", and its title, and add to
++   it an item stating at least the title, year, new authors, and
++   publisher of the Modified Version as given on the Title Page.  If
++   there is no section entitled "History" in the Document, create one
++   stating the title, year, authors, and publisher of the Document as
++   given on its Title Page, then add an item describing the Modified
++   Version as stated in the previous sentence.
++J. Preserve the network location, if any, given in the Document for
++   public access to a Transparent copy of the Document, and likewise
++   the network locations given in the Document for previous versions
++   it was based on.  These may be placed in the "History" section.
++   You may omit a network location for a work that was published at
++   least four years before the Document itself, or if the original
++   publisher of the version it refers to gives permission.
++K. In any section entitled "Acknowledgements" or "Dedications",
++   preserve the section's title, and preserve in the section all the
++   substance and tone of each of the contributor acknowledgements
++   and/or dedications given therein.
++L. Preserve all the Invariant Sections of the Document,
++   unaltered in their text and in their titles.  Section numbers
++   or the equivalent are not considered part of the section titles.
++M. Delete any section entitled "Endorsements".  Such a section
++   may not be included in the Modified Version.
++N. Do not retitle any existing section as "Endorsements"
++   or to conflict in title with any Invariant Section.
++
++If the Modified Version includes new front-matter sections or
++appendices that qualify as Secondary Sections and contain no material
++copied from the Document, you may at your option designate some or all
++of these sections as invariant.  To do this, add their titles to the
++list of Invariant Sections in the Modified Version's license notice.
++These titles must be distinct from any other section titles.
++
++You may add a section entitled "Endorsements", provided it contains
++nothing but endorsements of your Modified Version by various
++parties--for example, statements of peer review or that the text has
++been approved by an organization as the authoritative definition of a
++standard.
++
++You may add a passage of up to five words as a Front-Cover Text, and a
++passage of up to 25 words as a Back-Cover Text, to the end of the list
++of Cover Texts in the Modified Version.  Only one passage of
++Front-Cover Text and one of Back-Cover Text may be added by (or
++through arrangements made by) any one entity.  If the Document already
++includes a cover text for the same cover, previously added by you or
++by arrangement made by the same entity you are acting on behalf of,
++you may not add another; but you may replace the old one, on explicit
++permission from the previous publisher that added the old one.
++
++The author(s) and publisher(s) of the Document do not by this License
++give permission to use their names for publicity for or to assert or
++imply endorsement of any Modified Version.
++
++
++5. COMBINING DOCUMENTS
++
++You may combine the Document with other documents released under this
++License, under the terms defined in section 4 above for modified
++versions, provided that you include in the combination all of the
++Invariant Sections of all of the original documents, unmodified, and
++list them all as Invariant Sections of your combined work in its
++license notice.
++
++The combined work need only contain one copy of this License, and
++multiple identical Invariant Sections may be replaced with a single
++copy.  If there are multiple Invariant Sections with the same name but
++different contents, make the title of each such section unique by
++adding at the end of it, in parentheses, the name of the original
++author or publisher of that section if known, or else a unique number.
++Make the same adjustment to the section titles in the list of
++Invariant Sections in the license notice of the combined work.
++
++In the combination, you must combine any sections entitled "History"
++in the various original documents, forming one section entitled
++"History"; likewise combine any sections entitled "Acknowledgements",
++and any sections entitled "Dedications".  You must delete all sections
++entitled "Endorsements."
++
++
++6. COLLECTIONS OF DOCUMENTS
++
++You may make a collection consisting of the Document and other documents
++released under this License, and replace the individual copies of this
++License in the various documents with a single copy that is included in
++the collection, provided that you follow the rules of this License for
++verbatim copying of each of the documents in all other respects.
++
++You may extract a single document from such a collection, and distribute
++it individually under this License, provided you insert a copy of this
++License into the extracted document, and follow this License in all
++other respects regarding verbatim copying of that document.
++
++
++7. AGGREGATION WITH INDEPENDENT WORKS
++
++A compilation of the Document or its derivatives with other separate
++and independent documents or works, in or on a volume of a storage or
++distribution medium, does not as a whole count as a Modified Version
++of the Document, provided no compilation copyright is claimed for the
++compilation.  Such a compilation is called an "aggregate", and this
++License does not apply to the other self-contained works thus compiled
++with the Document, on account of their being thus compiled, if they
++are not themselves derivative works of the Document.
++
++If the Cover Text requirement of section 3 is applicable to these
++copies of the Document, then if the Document is less than one quarter
++of the entire aggregate, the Document's Cover Texts may be placed on
++covers that surround only the Document within the aggregate.
++Otherwise they must appear on covers around the whole aggregate.
++
++
++8. TRANSLATION
++
++Translation is considered a kind of modification, so you may
++distribute translations of the Document under the terms of section 4.
++Replacing Invariant Sections with translations requires special
++permission from their copyright holders, but you may include
++translations of some or all Invariant Sections in addition to the
++original versions of these Invariant Sections.  You may include a
++translation of this License provided that you also include the
++original English version of this License.  In case of a disagreement
++between the translation and the original English version of this
++License, the original English version will prevail.
++
++
++9. TERMINATION
++
++You may not copy, modify, sublicense, or distribute the Document except
++as expressly provided for under this License.  Any other attempt to
++copy, modify, sublicense or distribute the Document 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.
++
++
++10. FUTURE REVISIONS OF THIS LICENSE
++
++The Free Software Foundation may publish new, revised versions
++of the GNU Free Documentation 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.  See
++http://www.gnu.org/copyleft/.
++
++Each version of the License is given a distinguishing version number.
++If the Document specifies that a particular numbered version of this
++License "or any later version" applies to it, you have the option of
++following the terms and conditions either of that specified version or
++of any later version that has been published (not as a draft) by the
++Free Software Foundation.  If the Document does not specify a version
++number of this License, you may choose any version ever published (not
++as a draft) by the Free Software Foundation.
++
++
++ADDENDUM: How to use this License for your documents
++
++To use this License in a document you have written, include a copy of
++the License in the document and put the following copyright and
++license notices just after the title page:
++
++@smallexample
++    Copyright (c)  YEAR  YOUR NAME.
++    Permission is granted to copy, distribute and/or modify this document
++    under the terms of the GNU Free Documentation License, Version 1.1
++    or any later version published by the Free Software Foundation;
++    with the Invariant Sections being LIST THEIR TITLES, with the
++    Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
++    A copy of the license is included in the section entitled "GNU
++    Free Documentation License".
++@end smallexample
++
++If you have no Invariant Sections, write "with no Invariant Sections"
++instead of saying which ones are invariant.  If you have no
++Front-Cover Texts, write "no Front-Cover Texts" instead of
++"Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
++
++If your document contains nontrivial examples of program code, we
++recommend releasing these examples in parallel under your choice of
++free software license, such as the GNU General Public License,
++to permit their use in free software.
++
++@node Index
++@unnumbered Index
++
++@printindex cp
++
++@contents
++@bye
+--- binutils-2.17.50.0.2/gas/gasp.c.orig       1970-01-01 01:00:00.000000000 +0100
++++ binutils-2.17.50.0.2/gas/gasp.c    2006-06-07 11:22:13.774440000 +0200
+@@ -0,0 +1,3999 @@
++/* gasp.c - Gnu assembler preprocessor main program.
++   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
++   Free Software Foundation, Inc.
++
++   Written by Steve and Judy Chamberlain of Cygnus Support,
++      sac@cygnus.com
++
++   This file is part of GASP, the GNU Assembler Preprocessor.
++
++   GASP is free software; you can redistribute it and/or modify
++   it under the terms of the GNU General Public License as published by
++   the Free Software Foundation; either version 2, or (at your option)
++   any later version.
++
++   GASP is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++   GNU General Public License for more details.
++
++   You should have received a copy of the GNU General Public License
++   along with GASP; see the file COPYING.  If not, write to the Free
++   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
++   02111-1307, USA.  */
++
++/*
++This program translates the input macros and stuff into a form
++suitable for gas to consume.
++
++  gasp [-sdhau] [-c char] [-o <outfile>] <infile>*
++
++  -s copy source to output
++  -c <char> comments are started with <char> instead of !
++  -u allow unreasonable stuff
++  -p print line numbers
++  -d print debugging stats
++  -s semi colons start comments
++  -a use alternate syntax
++     Pseudo ops can start with or without a .
++     Labels have to be in first column.
++  -I specify include dir
++    Macro arg parameters subsituted by name, don't need the &.
++     String can start with ' too.
++     Strings can be surrounded by <..>
++     A %<exp> in a string evaluates the expression
++     Literal char in a string with !
++*/
++
++#include "config.h"
++#include "bfdver.h"
++
++#include <assert.h>
++#include <stdio.h>
++#include <string.h>
++#include "getopt.h"
++
++#ifdef HAVE_STDLIB_H
++#include <stdlib.h>
++#endif
++
++#ifdef NEED_MALLOC_DECLARATION
++extern char *malloc ();
++#endif
++
++#include "ansidecl.h"
++#include "libiberty.h"
++#include "safe-ctype.h"
++#include "sb.h"
++#include "macro.h"
++#include "asintl.h"
++#include "xregex.h"
++
++char *program_version = "1.2";
++
++/* This is normally declared in as.h, but we don't include that.  We
++   need the function because other files linked with gasp.c might call
++   it.  */
++extern void as_abort PARAMS ((const char *, int, const char *));
++
++/* The default obstack chunk size.  If we set this to zero, the
++   obstack code will use whatever will fit in a 4096 byte block.  This
++   is used by the hash table code used by macro.c.  */
++int chunksize = 0;
++
++#define MAX_INCLUDES 30               /* Maximum include depth.  */
++#define MAX_REASONABLE 1000   /* Maximum number of expansions.  */
++
++int unreasonable;             /* -u on command line.  */
++int stats;                    /* -d on command line.  */
++int print_line_number;                /* -p flag on command line.  */
++int copysource;                       /* -c flag on command line.  */
++int warnings;                 /* Number of WARNINGs generated so far.  */
++int errors;                   /* Number of ERRORs generated so far.  */
++int fatals;                   /* Number of fatal ERRORs generated so far (either 0 or 1).  */
++int alternate = 0;              /* -a on command line.  */
++int mri = 0;                  /* -M on command line.  */
++char comment_char = '!';
++int radix = 10;                       /* Default radix.  */
++
++int had_end; /* Seen .END.  */
++
++/* The output stream.  */
++FILE *outfile;
++
++/* The attributes of each character are stored as a bit pattern
++   chartype, which gives us quick tests.  */
++
++#define FIRSTBIT 1
++#define NEXTBIT  2
++#define SEPBIT   4
++#define WHITEBIT 8
++#define COMMENTBIT 16
++#define BASEBIT  32
++#define ISCOMMENTCHAR(x) (chartype[(unsigned char)(x)] & COMMENTBIT)
++#define ISFIRSTCHAR(x)  (chartype[(unsigned char)(x)] & FIRSTBIT)
++#define ISNEXTCHAR(x)   (chartype[(unsigned char)(x)] & NEXTBIT)
++#define ISSEP(x)        (chartype[(unsigned char)(x)] & SEPBIT)
++#define ISWHITE(x)      (chartype[(unsigned char)(x)] & WHITEBIT)
++#define ISBASE(x)       (chartype[(unsigned char)(x)] & BASEBIT)
++static char chartype[256];
++
++/* Conditional assembly uses the `ifstack'.  Each aif pushes another
++   entry onto the stack, and sets the on flag if it should.  The aelse
++   sets hadelse, and toggles on.  An aend pops a level.  We limit to
++   100 levels of nesting, not because we're facists pigs with read
++   only minds, but because more than 100 levels of nesting is probably
++   a bug in the user's macro structure.  */
++
++#define IFNESTING 100
++struct {
++  int on;                     /* Is the level being output.  */
++  int hadelse;                        /* Has an aelse been seen.  */
++} ifstack[IFNESTING];
++
++int ifi;
++
++/* The final and intermediate results of expression evaluation are kept in
++   exp_t's.  Note that a symbol is not an sb, but a pointer into the input
++   line.  It must be coped somewhere safe before the next line is read in.  */
++
++typedef struct {
++  char *name;
++  int len;
++} symbol;
++
++typedef struct {
++  int value;                  /* Constant part.  */
++  symbol add_symbol;          /* Name part.  */
++  symbol sub_symbol;          /* Name part.  */
++} exp_t;
++
++/* Hashing is done in a pretty standard way.  A hash_table has a
++   pointer to a vector of pointers to hash_entrys, and the size of the
++   vector.  A hash_entry contains a union of all the info we like to
++   store in hash table.  If there is a hash collision, hash_entries
++   with the same hash are kept in a chain.  */
++
++/* What the data in a hash_entry means.  */
++typedef enum {
++  hash_integer,                       /* Name->integer mapping.  */
++  hash_string,                        /* Name->string mapping.  */
++  hash_macro,                 /* Name is a macro.  */
++  hash_formal                 /* Name is a formal argument.  */
++} hash_type;
++
++typedef struct hs {
++  sb key;                     /* Symbol name.  */
++  hash_type type;             /* Symbol meaning.  */
++  union {
++    sb s;
++    int i;
++    struct macro_struct *m;
++    struct formal_struct *f;
++  } value;
++  struct hs *next;            /* Next hash_entry with same hash key.  */
++} hash_entry;
++
++typedef struct {
++  hash_entry **table;
++  int size;
++} hash_table;
++
++/* How we nest files and expand macros etc.
++
++   We keep a stack of of include_stack structs.  Each include file
++   pushes a new level onto the stack.  We keep an sb with a pushback
++   too.  unget chars are pushed onto the pushback sb, getchars first
++   checks the pushback sb before reading from the input stream.
++
++   Small things are expanded by adding the text of the item onto the
++   pushback sb.  Larger items are grown by pushing a new level and
++   allocating the entire pushback buf for the item.  Each time
++   something like a macro is expanded, the stack index is changed.  We
++   can then perform an exitm by popping all entries off the stack with
++   the same stack index.  If we're being reasonable, we can detect
++   recusive expansion by checking the index is reasonably small.  */
++
++typedef enum {
++  include_file, include_repeat, include_while, include_macro
++} include_type;
++
++struct include_stack {
++  sb pushback;                        /* Current pushback stream.  */
++  int pushback_index;         /* Next char to read from stream.  */
++  FILE *handle;                       /* Open file.  */
++  sb name;                    /* Name of file.  */
++  int linecount;              /* Number of lines read so far.  */
++  include_type type;
++  int index;                  /* Index of this layer.  */
++} include_stack[MAX_INCLUDES];
++
++struct include_stack *sp;
++#define isp (sp - include_stack)
++
++/* Include file list.  */
++
++typedef struct include_path {
++  struct include_path *next;
++  sb path;
++} include_path;
++
++include_path *paths_head;
++include_path *paths_tail;
++
++static void quit PARAMS ((void));
++static void hash_new_table PARAMS ((int, hash_table *));
++static int hash PARAMS ((sb *));
++static hash_entry *hash_create PARAMS ((hash_table *, sb *));
++static void hash_add_to_string_table PARAMS ((hash_table *, sb *, sb *, int));
++static void hash_add_to_int_table PARAMS ((hash_table *, sb *, int));
++static hash_entry *hash_lookup PARAMS ((hash_table *, sb *));
++static void checkconst PARAMS ((int, exp_t *));
++static int is_flonum PARAMS ((int, sb *));
++static int chew_flonum PARAMS ((int, sb *, sb *));
++static int sb_strtol PARAMS ((int, sb *, int, int *));
++static int level_0 PARAMS ((int, sb *, exp_t *));
++static int level_1 PARAMS ((int, sb *, exp_t *));
++static int level_2 PARAMS ((int, sb *, exp_t *));
++static int level_3 PARAMS ((int, sb *, exp_t *));
++static int level_4 PARAMS ((int, sb *, exp_t *));
++static int level_5 PARAMS ((int, sb *, exp_t *));
++static int exp_parse PARAMS ((int, sb *, exp_t *));
++static void exp_string PARAMS ((exp_t *, sb *));
++static int exp_get_abs PARAMS ((const char *, int, sb *, int *));
++#if 0
++static void strip_comments PARAMS ((sb *));
++#endif
++static void unget PARAMS ((int));
++static void include_buf PARAMS ((sb *, sb *, include_type, int));
++static void include_print_where_line PARAMS ((FILE *));
++static void include_print_line PARAMS ((FILE *));
++static int get_line PARAMS ((sb *));
++static int grab_label PARAMS ((sb *, sb *));
++static void change_base PARAMS ((int, sb *, sb *));
++static void do_end PARAMS ((sb *));
++static void do_assign PARAMS ((int, int, sb *));
++static void do_radix PARAMS ((sb *));
++static int get_opsize PARAMS ((int, sb *, int *));
++static int eol PARAMS ((int, sb *));
++static void do_data PARAMS ((int, sb *, int));
++static void do_datab PARAMS ((int, sb *));
++static void do_align PARAMS ((int, sb *));
++static void do_res PARAMS ((int, sb *, int));
++static void do_export PARAMS ((sb *));
++static void do_print PARAMS ((int, sb *));
++static void do_heading PARAMS ((int, sb *));
++static void do_page PARAMS ((void));
++static void do_form PARAMS ((int, sb *));
++static int get_any_string PARAMS ((int, sb *, sb *, int, int));
++static int skip_openp PARAMS ((int, sb *));
++static int skip_closep PARAMS ((int, sb *));
++static int dolen PARAMS ((int, sb *, sb *));
++static int doinstr PARAMS ((int, sb *, sb *));
++static int dosubstr PARAMS ((int, sb *, sb *));
++static void process_assigns PARAMS ((int, sb *, sb *));
++static int get_and_process PARAMS ((int, sb *, sb *));
++static void process_file PARAMS ((void));
++static void free_old_entry PARAMS ((hash_entry *));
++static void do_assigna PARAMS ((int, sb *));
++static void do_assignc PARAMS ((int, sb *));
++static void do_reg PARAMS ((int, sb *));
++static int condass_lookup_name PARAMS ((sb *, int, sb *, int));
++static int whatcond PARAMS ((int, sb *, int *));
++static int istrue PARAMS ((int, sb *));
++static void do_aif PARAMS ((int, sb *));
++static void do_aelse PARAMS ((void));
++static void do_aendi PARAMS ((void));
++static int condass_on PARAMS ((void));
++static void do_if PARAMS ((int, sb *, int));
++static int get_mri_string PARAMS ((int, sb *, sb *, int));
++static void do_ifc PARAMS ((int, sb *, int));
++static void do_aendr PARAMS ((void));
++static void do_awhile PARAMS ((int, sb *));
++static void do_aendw PARAMS ((void));
++static void do_exitm PARAMS ((void));
++static void do_arepeat PARAMS ((int, sb *));
++static void do_endm PARAMS ((void));
++static void do_irp PARAMS ((int, sb *, int));
++static void do_local PARAMS ((int, sb *));
++static void do_macro PARAMS ((int, sb *));
++static int macro_op PARAMS ((int, sb *));
++static int getstring PARAMS ((int, sb *, sb *));
++static void do_sdata PARAMS ((int, sb *, int));
++static void do_sdatab PARAMS ((int, sb *));
++static int new_file PARAMS ((const char *));
++static void do_include PARAMS ((int, sb *));
++static void include_pop PARAMS ((void));
++static int get PARAMS ((void));
++static int linecount PARAMS ((void));
++static int include_next_index PARAMS ((void));
++static void chartype_init PARAMS ((void));
++static int process_pseudo_op PARAMS ((int, sb *, sb *));
++static void add_keyword PARAMS ((const char *, int));
++static void process_init PARAMS ((void));
++static void do_define PARAMS ((const char *));
++static void show_usage PARAMS ((FILE *, int));
++static void show_help PARAMS ((void));
++
++/* --- functions recently removed from sb.c --- */
++
++/* put a null at the end of the sb at in and return the start of the
++ *    string, so that it can be used as an arg to printf %s.  */
++
++static char *
++sb_name (sb *in)
++{
++  /* stick a null on the end of the string */
++  sb_add_char (in, 0);
++  return in->ptr;
++}
++
++/* print the sb at ptr to the output file */
++
++static void
++sb_print (FILE *outfile, sb *ptr)
++{
++  int i;
++  int nc = 0;
++
++  for (i = 0; i < ptr->len; i++)
++    {
++      if (nc)
++      {
++        fprintf (outfile, ",");
++      }
++      fprintf (outfile, "%d", ptr->ptr[i]);
++      nc = 1;
++    }
++}
++
++/* print the sb at ptr to the output file */
++
++static void
++sb_print_at (FILE *outfile, int idx, sb *ptr)
++{
++  int i;
++  for (i = idx; i < ptr->len; i++)
++    putc (ptr->ptr[i], outfile);
++}
++
++#define FATAL(x)                              \
++  do                                          \
++    {                                         \
++      include_print_where_line (stderr);      \
++      fprintf x;                              \
++      fatals++;                                       \
++      quit ();                                        \
++    }                                         \
++  while (0)
++
++#define ERROR(x)                              \
++  do                                          \
++    {                                         \
++      include_print_where_line (stderr);      \
++      fprintf x;                              \
++      errors++;                                       \
++    }                                         \
++  while (0)
++
++#define WARNING(x)                            \
++  do                                          \
++    {                                         \
++      include_print_where_line (stderr);      \
++      fprintf x;                              \
++      warnings++;                             \
++    }                                         \
++  while (0)
++
++/* Exit the program and return the right ERROR code.  */
++
++static void
++quit ()
++{
++  int exitcode;
++  if (fatals + errors)
++    exitcode = 1;
++  else
++    exitcode = 0;
++
++  exit (exitcode);
++}
++
++/* Hash table maintenance.  */
++
++/* Build a new hash table with size buckets
++   and fill in the info at ptr.  */
++
++static void
++hash_new_table (size, ptr)
++     int size;
++     hash_table *ptr;
++{
++  int i;
++  ptr->size = size;
++  ptr->table = (hash_entry **) xmalloc (size * (sizeof (hash_entry *)));
++  /* Fill with null-pointer, not zero-bit-pattern.  */
++  for (i = 0; i < size; i++)
++    ptr->table[i] = 0;
++}
++
++/* Calculate and return the hash value of the sb at key.  */
++
++static int
++hash (key)
++     sb *key;
++{
++  int k = 0x1234;
++  int i;
++  char *p = key->ptr;
++  for (i = 0; i < key->len; i++)
++    {
++      k ^= (k << 2) ^ *p;
++      p++;
++    }
++  return k & 0xf0fff;
++}
++
++/* Look up key in hash_table tab.  If present, then return it,
++   otherwise build a new one and fill it with hash_integer.  */
++
++static hash_entry *
++hash_create (tab, key)
++     hash_table *tab;
++     sb *key;
++{
++  int k = hash (key) % tab->size;
++  hash_entry *p;
++  hash_entry **table = tab->table;
++
++  p = table[k];
++
++  while (1)
++    {
++      if (!p)
++      {
++        hash_entry *n = (hash_entry *) xmalloc (sizeof (hash_entry));
++        n->next = table[k];
++        sb_new (&n->key);
++        sb_add_sb (&n->key, key);
++        table[k] = n;
++        n->type = hash_integer;
++        return n;
++      }
++      if (strncmp (table[k]->key.ptr, key->ptr, key->len) == 0)
++      {
++        return p;
++      }
++      p = p->next;
++    }
++}
++
++/* Add sb name with key into hash_table tab.
++   If replacing old value and again, then ERROR.  */
++
++static void
++hash_add_to_string_table (tab, key, name, again)
++     hash_table *tab;
++     sb *key;
++     sb *name;
++     int again;
++{
++  hash_entry *ptr = hash_create (tab, key);
++  if (ptr->type == hash_integer)
++    {
++      sb_new (&ptr->value.s);
++    }
++  if (ptr->value.s.len)
++    {
++      if (!again)
++      ERROR ((stderr, _("redefinition not allowed\n")));
++    }
++
++  ptr->type = hash_string;
++  sb_reset (&ptr->value.s);
++
++  sb_add_sb (&ptr->value.s, name);
++}
++
++/* Add integer name to hash_table tab with sb key.  */
++
++static void
++hash_add_to_int_table (tab, key, name)
++     hash_table *tab;
++     sb *key;
++     int name;
++{
++  hash_entry *ptr = hash_create (tab, key);
++  ptr->value.i = name;
++}
++
++/* Look up sb key in hash_table tab.
++   If found, return hash_entry result, else 0.  */
++
++static hash_entry *
++hash_lookup (tab, key)
++     hash_table *tab;
++     sb *key;
++{
++  int k = hash (key) % tab->size;
++  hash_entry **table = tab->table;
++  hash_entry *p = table[k];
++  while (p)
++    {
++      if (p->key.len == key->len
++        && strncmp (p->key.ptr, key->ptr, key->len) == 0)
++      return p;
++      p = p->next;
++    }
++  return 0;
++}
++
++/* expressions
++
++   are handled in a really simple recursive decent way. each bit of
++   the machine takes an index into an sb and a pointer to an exp_t,
++   modifies the *exp_t and returns the index of the first character
++   past the part of the expression parsed.
++
++ expression precedence:
++  ( )
++ unary + - ~
++  * /
++  + -
++  &
++  | ~
++*/
++
++/* Make sure that the exp_t at term is constant.
++   If not the give the op ERROR.  */
++
++static void
++checkconst (op, term)
++     int op;
++     exp_t *term;
++{
++  if (term->add_symbol.len
++      || term->sub_symbol.len)
++    {
++      ERROR ((stderr, _("the %c operator cannot take non-absolute arguments.\n"), op));
++    }
++}
++
++/* Chew the flonum from the string starting at idx.  Adjust idx to
++   point to the next character after the flonum.  */
++
++static int
++chew_flonum (idx, string, out)
++     int idx;
++     sb *string;
++     sb *out;
++{
++  sb buf;
++  regex_t reg;
++  regmatch_t match;
++
++  /* Duplicate and null terminate `string'.  */
++  sb_new (&buf);
++  sb_add_sb (&buf, string);
++  sb_add_char (&buf, '\0');
++
++  if (regcomp (&reg, "([0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?)", REG_EXTENDED) != 0)
++    return idx;
++  if (regexec (&reg, &buf.ptr[idx], 1, &match, 0) != 0)
++    return idx;
++
++  /* Copy the match to the output.  */
++  assert (match.rm_eo >= match.rm_so);
++  sb_add_buffer (out, &buf.ptr[idx], match.rm_eo - match.rm_so);
++
++  sb_kill (&buf);
++  regfree (&reg);
++  idx += match.rm_eo;
++  return idx;
++}
++
++static int
++is_flonum (idx, string)
++     int idx;
++     sb *string;
++{
++  sb buf;
++  regex_t reg;
++  int rc;
++
++  /* Duplicate and null terminate `string'.  */
++  sb_new (&buf);
++  sb_add_sb (&buf, string);
++  sb_add_char (&buf, '\0');
++
++  if (regcomp (&reg, "^[0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?", REG_EXTENDED) != 0)
++    return 0;
++
++  rc = regexec (&reg, &buf.ptr[idx], 0, NULL, 0);
++  sb_kill (&buf);
++  regfree (&reg);
++  return (rc == 0);
++}
++
++/* Turn the number in string at idx into a number of base, fill in
++   ptr, and return the index of the first character not in the number.  */
++
++static int
++sb_strtol (idx, string, base, ptr)
++     int idx;
++     sb *string;
++     int base;
++     int *ptr;
++{
++  int value = 0;
++  idx = sb_skip_white (idx, string);
++
++  while (idx < string->len)
++    {
++      int ch = string->ptr[idx];
++      int dig = 0;
++      if (ISDIGIT (ch))
++      dig = ch - '0';
++      else if (ch >= 'a' && ch <= 'f')
++      dig = ch - 'a' + 10;
++      else if (ch >= 'A' && ch <= 'F')
++      dig = ch - 'A' + 10;
++      else
++      break;
++
++      if (dig >= base)
++      break;
++
++      value = value * base + dig;
++      idx++;
++    }
++  *ptr = value;
++  return idx;
++}
++
++static int
++level_0 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  lhs->add_symbol.len = 0;
++  lhs->add_symbol.name = 0;
++
++  lhs->sub_symbol.len = 0;
++  lhs->sub_symbol.name = 0;
++
++  idx = sb_skip_white (idx, string);
++
++  lhs->value = 0;
++
++  if (ISDIGIT (string->ptr[idx]))
++    {
++      idx = sb_strtol (idx, string, 10, &lhs->value);
++    }
++  else if (ISFIRSTCHAR (string->ptr[idx]))
++    {
++      int len = 0;
++      lhs->add_symbol.name = string->ptr + idx;
++      while (idx < string->len && ISNEXTCHAR (string->ptr[idx]))
++      {
++        idx++;
++        len++;
++      }
++      lhs->add_symbol.len = len;
++    }
++  else if (string->ptr[idx] == '"')
++    {
++      sb acc;
++      sb_new (&acc);
++      ERROR ((stderr, _("string where expression expected.\n")));
++      idx = getstring (idx, string, &acc);
++      sb_kill (&acc);
++    }
++  else
++    {
++      ERROR ((stderr, _("can't find primary in expression.\n")));
++      idx++;
++    }
++  return sb_skip_white (idx, string);
++}
++
++static int
++level_1 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  idx = sb_skip_white (idx, string);
++
++  switch (string->ptr[idx])
++    {
++    case '+':
++      idx = level_1 (idx + 1, string, lhs);
++      break;
++    case '~':
++      idx = level_1 (idx + 1, string, lhs);
++      checkconst ('~', lhs);
++      lhs->value = ~lhs->value;
++      break;
++    case '-':
++      {
++      symbol t;
++      idx = level_1 (idx + 1, string, lhs);
++      lhs->value = -lhs->value;
++      t = lhs->add_symbol;
++      lhs->add_symbol = lhs->sub_symbol;
++      lhs->sub_symbol = t;
++      break;
++      }
++    case '(':
++      idx++;
++      idx = level_5 (sb_skip_white (idx, string), string, lhs);
++      if (string->ptr[idx] != ')')
++      ERROR ((stderr, _("misplaced closing parens.\n")));
++      else
++      idx++;
++      break;
++    default:
++      idx = level_0 (idx, string, lhs);
++      break;
++    }
++  return sb_skip_white (idx, string);
++}
++
++static int
++level_2 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  exp_t rhs;
++
++  idx = level_1 (idx, string, lhs);
++
++  while (idx < string->len && (string->ptr[idx] == '*'
++                             || string->ptr[idx] == '/'))
++    {
++      char op = string->ptr[idx++];
++      idx = level_1 (idx, string, &rhs);
++      switch (op)
++      {
++      case '*':
++        checkconst ('*', lhs);
++        checkconst ('*', &rhs);
++        lhs->value *= rhs.value;
++        break;
++      case '/':
++        checkconst ('/', lhs);
++        checkconst ('/', &rhs);
++        if (rhs.value == 0)
++          ERROR ((stderr, _("attempt to divide by zero.\n")));
++        else
++          lhs->value /= rhs.value;
++        break;
++      }
++    }
++  return sb_skip_white (idx, string);
++}
++
++static int
++level_3 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  exp_t rhs;
++
++  idx = level_2 (idx, string, lhs);
++
++  while (idx < string->len
++       && (string->ptr[idx] == '+'
++           || string->ptr[idx] == '-'))
++    {
++      char op = string->ptr[idx++];
++      idx = level_2 (idx, string, &rhs);
++      switch (op)
++      {
++      case '+':
++        lhs->value += rhs.value;
++        if (lhs->add_symbol.name && rhs.add_symbol.name)
++          {
++            ERROR ((stderr, _("can't add two relocatable expressions\n")));
++          }
++        /* Change nn+symbol to symbol + nn.  */
++        if (rhs.add_symbol.name)
++          {
++            lhs->add_symbol = rhs.add_symbol;
++          }
++        break;
++      case '-':
++        lhs->value -= rhs.value;
++        lhs->sub_symbol = rhs.add_symbol;
++        break;
++      }
++    }
++  return sb_skip_white (idx, string);
++}
++
++static int
++level_4 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  exp_t rhs;
++
++  idx = level_3 (idx, string, lhs);
++
++  while (idx < string->len &&
++       string->ptr[idx] == '&')
++    {
++      char op = string->ptr[idx++];
++      idx = level_3 (idx, string, &rhs);
++      switch (op)
++      {
++      case '&':
++        checkconst ('&', lhs);
++        checkconst ('&', &rhs);
++        lhs->value &= rhs.value;
++        break;
++      }
++    }
++  return sb_skip_white (idx, string);
++}
++
++static int
++level_5 (idx, string, lhs)
++     int idx;
++     sb *string;
++     exp_t *lhs;
++{
++  exp_t rhs;
++
++  idx = level_4 (idx, string, lhs);
++
++  while (idx < string->len
++       && (string->ptr[idx] == '|' || string->ptr[idx] == '~'))
++    {
++      char op = string->ptr[idx++];
++      idx = level_4 (idx, string, &rhs);
++      switch (op)
++      {
++      case '|':
++        checkconst ('|', lhs);
++        checkconst ('|', &rhs);
++        lhs->value |= rhs.value;
++        break;
++      case '~':
++        checkconst ('~', lhs);
++        checkconst ('~', &rhs);
++        lhs->value ^= rhs.value;
++        break;
++      }
++    }
++  return sb_skip_white (idx, string);
++}
++
++/* Parse the expression at offset idx into string, fill up res with
++   the result.  Return the index of the first char past the
++   expression.  */
++
++static int
++exp_parse (idx, string, res)
++     int idx;
++     sb *string;
++     exp_t *res;
++{
++  return level_5 (sb_skip_white (idx, string), string, res);
++}
++
++/* Turn the expression at exp into text and glue it onto the end of
++   string.  */
++
++static void
++exp_string (exp, string)
++     exp_t *exp;
++     sb *string;
++{
++  int np = 0;
++  int ad = 0;
++  sb_reset (string);
++
++  if (exp->add_symbol.len)
++    {
++      sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len);
++      np = 1;
++      ad = 1;
++    }
++  if (exp->value)
++    {
++      char buf[20];
++      if (np)
++      sb_add_char (string, '+');
++      sprintf (buf, "%d", exp->value);
++      sb_add_string (string, buf);
++      np = 1;
++      ad = 1;
++    }
++  if (exp->sub_symbol.len)
++    {
++      sb_add_char (string, '-');
++      sb_add_buffer (string, exp->add_symbol.name, exp->add_symbol.len);
++      np = 0;
++      ad = 1;
++    }
++
++  if (!ad)
++    sb_add_char (string, '0');
++}
++
++/* Parse the expression at offset idx into sb in.  Return the value in
++   val.  If the expression is not constant, give ERROR emsg.  Return
++   the index of the first character past the end of the expression.  */
++
++static int
++exp_get_abs (emsg, idx, in, val)
++     const char *emsg;
++     int idx;
++     sb *in;
++     int *val;
++{
++  exp_t res;
++  idx = exp_parse (idx, in, &res);
++  if (res.add_symbol.len || res.sub_symbol.len)
++    ERROR ((stderr, "%s", emsg));
++  *val = res.value;
++  return idx;
++}
++
++/* Current label parsed from line.  */
++sb label;
++
++/* Hash table for all assigned variables.  */
++hash_table assign_hash_table;
++
++/* Hash table for keyword.  */
++hash_table keyword_hash_table;
++
++/* Hash table for eq variables.  */
++hash_table vars;
++
++#define in_comment ';'
++
++#if 0
++static void
++strip_comments (out)
++     sb *out;
++{
++  char *s = out->ptr;
++  int i = 0;
++  for (i = 0; i < out->len; i++)
++    {
++      if (ISCOMMENTCHAR (s[i]))
++      {
++        out->len = i;
++        return;
++      }
++    }
++}
++#endif
++
++/* Push back character ch so that it can be read again.  */
++
++static void
++unget (ch)
++     int ch;
++{
++  if (ch == '\n')
++    {
++      sp->linecount--;
++    }
++  if (sp->pushback_index)
++    sp->pushback_index--;
++  else
++    sb_add_char (&sp->pushback, ch);
++}
++
++/* Push the sb ptr onto the include stack, with the given name, type
++   and index.  */
++
++static void
++include_buf (name, ptr, type, index)
++     sb *name;
++     sb *ptr;
++     include_type type;
++     int index;
++{
++  sp++;
++  if (sp - include_stack >= MAX_INCLUDES)
++    FATAL ((stderr, _("unreasonable nesting.\n")));
++  sb_new (&sp->name);
++  sb_add_sb (&sp->name, name);
++  sp->handle = 0;
++  sp->linecount = 1;
++  sp->pushback_index = 0;
++  sp->type = type;
++  sp->index = index;
++  sb_new (&sp->pushback);
++  sb_add_sb (&sp->pushback, ptr);
++}
++
++/* Used in ERROR messages, print info on where the include stack is
++   onto file.  */
++
++static void
++include_print_where_line (file)
++     FILE *file;
++{
++  struct include_stack *p = include_stack + 1;
++
++  while (p <= sp)
++    {
++      fprintf (file, "%s:%d ", sb_name (&p->name), p->linecount - 1);
++      p++;
++    }
++}
++
++/* Used in listings, print the line number onto file.  */
++
++static void
++include_print_line (file)
++     FILE *file;
++{
++  int n;
++  struct include_stack *p = include_stack + 1;
++
++  n = fprintf (file, "%4d", p->linecount);
++  p++;
++  while (p <= sp)
++    {
++      n += fprintf (file, ".%d", p->linecount);
++      p++;
++    }
++  while (n < 8 * 3)
++    {
++      fprintf (file, " ");
++      n++;
++    }
++}
++
++/* Read a line from the top of the include stack into sb in.  */
++
++static int
++get_line (in)
++     sb *in;
++{
++  int online = 0;
++  int more = 1, ch = 0;
++
++  if (copysource)
++    {
++      putc (comment_char, outfile);
++      if (print_line_number)
++      include_print_line (outfile);
++    }
++
++  while (1)
++    {
++      ch = get ();
++
++      while (ch == '\r')
++      ch = get ();
++
++      if (ch == EOF)
++      {
++        if (online)
++          {
++            WARNING ((stderr, _("End of file not at start of line.\n")));
++            if (copysource)
++              putc ('\n', outfile);
++            ch = '\n';
++          }
++        else
++          more = 0;
++        break;
++      }
++
++      if (copysource)
++      {
++        putc (ch, outfile);
++      }
++
++      if (ch == '\n')
++      {
++        ch = get ();
++        online = 0;
++        if (ch == '+')
++          {
++            /* Continued line.  */
++            if (copysource)
++              {
++                putc (comment_char, outfile);
++                putc ('+', outfile);
++              }
++            ch = get ();
++          }
++        else
++          {
++            if (ch != EOF)
++              unget (ch);
++            ch = '\n'; break;
++          }
++      }
++      else
++      {
++        sb_add_char (in, ch);
++      }
++      online++;
++    }
++
++  return more ? ch : 0;
++}
++
++/* Find a label from sb in and put it in out.  */
++
++static int
++grab_label (in, out)
++     sb *in;
++     sb *out;
++{
++  int i = 0;
++  sb_reset (out);
++  if (ISFIRSTCHAR (in->ptr[i]) || in->ptr[i] == '\\')
++    {
++      sb_add_char (out, in->ptr[i]);
++      i++;
++      while ((ISNEXTCHAR (in->ptr[i])
++            || in->ptr[i] == '\\'
++            || in->ptr[i] == '&')
++           && i < in->len)
++      {
++        sb_add_char (out, in->ptr[i]);
++        i++;
++      }
++    }
++  return i;
++}
++
++/* Find all strange base stuff and turn into decimal.  Also
++   find all the other numbers and convert them from the default radix.  */
++
++static void
++change_base (idx, in, out)
++     int idx;
++     sb *in;
++     sb *out;
++{
++  char buffer[20];
++
++  while (idx < in->len)
++    {
++      if (in->ptr[idx] == '\\'
++        && idx + 1 < in->len
++        && in->ptr[idx + 1] == '(')
++      {
++        idx += 2;
++        while (idx < in->len
++               && in->ptr[idx] != ')')
++          {
++            sb_add_char (out, in->ptr[idx]);
++            idx++;
++          }
++        if (idx < in->len)
++          idx++;
++      }
++      else if (idx < in->len - 1 && in->ptr[idx + 1] == '\'' && ! mri)
++      {
++        int base;
++        int value;
++        switch (in->ptr[idx])
++          {
++          case 'b':
++          case 'B':
++            base = 2;
++            break;
++          case 'q':
++          case 'Q':
++            base = 8;
++            break;
++          case 'h':
++          case 'H':
++            base = 16;
++            break;
++          case 'd':
++          case 'D':
++            base = 10;
++            break;
++          default:
++            ERROR ((stderr, _("Illegal base character %c.\n"), in->ptr[idx]));
++            base = 10;
++            break;
++          }
++
++        idx = sb_strtol (idx + 2, in, base, &value);
++        sprintf (buffer, "%d", value);
++        sb_add_string (out, buffer);
++      }
++      else if (ISFIRSTCHAR (in->ptr[idx]))
++      {
++        /* Copy entire names through quickly.  */
++        sb_add_char (out, in->ptr[idx]);
++        idx++;
++        while (idx < in->len && ISNEXTCHAR (in->ptr[idx]))
++          {
++            sb_add_char (out, in->ptr[idx]);
++            idx++;
++          }
++      }
++      else if (is_flonum (idx, in))
++      {
++        idx = chew_flonum (idx, in, out);
++      }
++      else if (ISDIGIT (in->ptr[idx]))
++      {
++        int value;
++        /* All numbers must start with a digit, let's chew it and
++           spit out decimal.  */
++        idx = sb_strtol (idx, in, radix, &value);
++        sprintf (buffer, "%d", value);
++        sb_add_string (out, buffer);
++
++        /* Skip all undigsested letters.  */
++        while (idx < in->len && ISNEXTCHAR (in->ptr[idx]))
++          {
++            sb_add_char (out, in->ptr[idx]);
++            idx++;
++          }
++      }
++      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
++      {
++        char tchar = in->ptr[idx];
++        /* Copy entire names through quickly.  */
++        sb_add_char (out, in->ptr[idx]);
++        idx++;
++        while (idx < in->len && in->ptr[idx] != tchar)
++          {
++            sb_add_char (out, in->ptr[idx]);
++            idx++;
++          }
++      }
++      else
++      {
++        /* Nothing special, just pass it through.  */
++        sb_add_char (out, in->ptr[idx]);
++        idx++;
++      }
++    }
++
++}
++
++/* .end  */
++
++static void
++do_end (in)
++     sb *in;
++{
++  had_end = 1;
++  if (mri)
++    fprintf (outfile, "%s\n", sb_name (in));
++}
++
++/* .assign  */
++
++static void
++do_assign (again, idx, in)
++     int again;
++     int idx;
++     sb *in;
++{
++  /* Stick label in symbol table with following value.  */
++  exp_t e;
++  sb acc;
++
++  sb_new (&acc);
++  idx = exp_parse (idx, in, &e);
++  exp_string (&e, &acc);
++  hash_add_to_string_table (&assign_hash_table, &label, &acc, again);
++  sb_kill (&acc);
++}
++
++/* .radix [b|q|d|h]  */
++
++static void
++do_radix (ptr)
++     sb *ptr;
++{
++  int idx = sb_skip_white (0, ptr);
++  switch (ptr->ptr[idx])
++    {
++    case 'B':
++    case 'b':
++      radix = 2;
++      break;
++    case 'q':
++    case 'Q':
++      radix = 8;
++      break;
++    case 'd':
++    case 'D':
++      radix = 10;
++      break;
++    case 'h':
++    case 'H':
++      radix = 16;
++      break;
++    default:
++      ERROR ((stderr, _("radix is %c must be one of b, q, d or h"), radix));
++    }
++}
++
++/* Parse off a .b, .w or .l.  */
++
++static int
++get_opsize (idx, in, size)
++     int idx;
++     sb *in;
++     int *size;
++{
++  *size = 4;
++  if (in->ptr[idx] == '.')
++    {
++      idx++;
++    }
++  switch (in->ptr[idx])
++    {
++    case 'b':
++    case 'B':
++      *size = 1;
++      break;
++    case 'w':
++    case 'W':
++      *size = 2;
++      break;
++    case 'l':
++    case 'L':
++      *size = 4;
++      break;
++    case ' ':
++    case '\t':
++      break;
++    default:
++      ERROR ((stderr, _("size must be one of b, w or l, is %c.\n"), in->ptr[idx]));
++      break;
++    }
++  idx++;
++
++  return idx;
++}
++
++static int
++eol (idx, line)
++     int idx;
++     sb *line;
++{
++  idx = sb_skip_white (idx, line);
++  if (idx < line->len
++      && ISCOMMENTCHAR(line->ptr[idx]))
++    return 1;
++  if (idx >= line->len)
++    return 1;
++  return 0;
++}
++
++/* .data [.b|.w|.l] <data>*
++    or d[bwl] <data>*  */
++
++static void
++do_data (idx, in, size)
++     int idx;
++     sb *in;
++     int size;
++{
++  int opsize = 4;
++  char *opname = ".yikes!";
++  sb acc;
++  sb_new (&acc);
++
++  if (!size)
++    {
++      idx = get_opsize (idx, in, &opsize);
++    }
++  else
++    {
++      opsize = size;
++    }
++  switch (opsize)
++    {
++    case 4:
++      opname = ".long";
++      break;
++    case 2:
++      opname = ".short";
++      break;
++    case 1:
++      opname = ".byte";
++      break;
++    }
++
++  fprintf (outfile, "%s\t", opname);
++
++  idx = sb_skip_white (idx, in);
++
++  if (alternate
++      && idx < in->len
++      && in->ptr[idx] == '"')
++    {
++      int i;
++      idx = getstring (idx, in, &acc);
++      for (i = 0; i < acc.len; i++)
++      {
++        if (i)
++          fprintf (outfile, ",");
++        fprintf (outfile, "%d", acc.ptr[i]);
++      }
++    }
++  else
++    {
++      while (!eol (idx, in))
++      {
++        exp_t e;
++        idx = exp_parse (idx, in, &e);
++        exp_string (&e, &acc);
++        sb_add_char (&acc, 0);
++        fprintf (outfile, "%s", acc.ptr);
++        if (idx < in->len && in->ptr[idx] == ',')
++          {
++            fprintf (outfile, ",");
++            idx++;
++          }
++      }
++    }
++  sb_kill (&acc);
++  sb_print_at (outfile, idx, in);
++  fprintf (outfile, "\n");
++}
++
++/* .datab [.b|.w|.l] <repeat>,<fill>  */
++
++static void
++do_datab (idx, in)
++     int idx;
++     sb *in;
++{
++  int opsize;
++  int repeat;
++  int fill;
++
++  idx = get_opsize (idx, in, &opsize);
++
++  idx = exp_get_abs (_("datab repeat must be constant.\n"), idx, in, &repeat);
++  idx = sb_skip_comma (idx, in);
++  idx = exp_get_abs (_("datab data must be absolute.\n"), idx, in, &fill);
++
++  fprintf (outfile, ".fill\t%d,%d,%d\n", repeat, opsize, fill);
++}
++
++/* .align <size>  */
++
++static void
++do_align (idx, in)
++     int idx;
++     sb *in;
++{
++  int al, have_fill, fill;
++
++  idx = exp_get_abs (_("align needs absolute expression.\n"), idx, in, &al);
++  idx = sb_skip_white (idx, in);
++  have_fill = 0;
++  fill = 0;
++  if (! eol (idx, in))
++    {
++      idx = sb_skip_comma (idx, in);
++      idx = exp_get_abs (_(".align needs absolute fill value.\n"), idx, in,
++                       &fill);
++      have_fill = 1;
++    }
++
++  fprintf (outfile, ".align   %d", al);
++  if (have_fill)
++    fprintf (outfile, ",%d", fill);
++  fprintf (outfile, "\n");
++}
++
++/* .res[.b|.w|.l] <size>  */
++
++static void
++do_res (idx, in, type)
++     int idx;
++     sb *in;
++     int type;
++{
++  int size = 4;
++  int count = 0;
++
++  idx = get_opsize (idx, in, &size);
++  while (!eol (idx, in))
++    {
++      idx = sb_skip_white (idx, in);
++      if (in->ptr[idx] == ',')
++      idx++;
++      idx = exp_get_abs (_("res needs absolute expression for fill count.\n"), idx, in, &count);
++
++      if (type == 'c' || type == 'z')
++      count++;
++
++      fprintf (outfile, ".space       %d\n", count * size);
++    }
++}
++
++/* .export  */
++
++static void
++do_export (in)
++     sb *in;
++{
++  fprintf (outfile, ".global  %s\n", sb_name (in));
++}
++
++/* .print [list] [nolist]  */
++
++static void
++do_print (idx, in)
++     int idx;
++     sb *in;
++{
++  idx = sb_skip_white (idx, in);
++  while (idx < in->len)
++    {
++      if (strncasecmp (in->ptr + idx, "LIST", 4) == 0)
++      {
++        fprintf (outfile, ".list\n");
++        idx += 4;
++      }
++      else if (strncasecmp (in->ptr + idx, "NOLIST", 6) == 0)
++      {
++        fprintf (outfile, ".nolist\n");
++        idx += 6;
++      }
++      idx++;
++    }
++}
++
++/* .head  */
++
++static void
++do_heading (idx, in)
++     int idx;
++     sb *in;
++{
++  sb head;
++  sb_new (&head);
++  idx = getstring (idx, in, &head);
++  fprintf (outfile, ".title   \"%s\"\n", sb_name (&head));
++  sb_kill (&head);
++}
++
++/* .page  */
++
++static void
++do_page ()
++{
++  fprintf (outfile, ".eject\n");
++}
++
++/* .form [lin=<value>] [col=<value>]  */
++
++static void
++do_form (idx, in)
++     int idx;
++     sb *in;
++{
++  int lines = 60;
++  int columns = 132;
++  idx = sb_skip_white (idx, in);
++
++  while (idx < in->len)
++    {
++
++      if (strncasecmp (in->ptr + idx, "LIN=", 4) == 0)
++      {
++        idx += 4;
++        idx = exp_get_abs (_("form LIN= needs absolute expresssion.\n"), idx, in, &lines);
++      }
++
++      if (strncasecmp (in->ptr + idx, _("COL="), 4) == 0)
++      {
++        idx += 4;
++        idx = exp_get_abs (_("form COL= needs absolute expresssion.\n"), idx, in, &columns);
++      }
++
++      idx++;
++    }
++  fprintf (outfile, ".psize %d,%d\n", lines, columns);
++
++}
++
++/* Fetch string from the input stream,
++   rules:
++    'Bxyx<whitespace>         -> return 'Bxyza
++    %<char>           -> return string of decimal value of x
++    "<string>"                -> return string
++    xyx<whitespace>     -> return xyz
++*/
++
++static int
++get_any_string (idx, in, out, expand, pretend_quoted)
++     int idx;
++     sb *in;
++     sb *out;
++     int expand;
++     int pretend_quoted;
++{
++  sb_reset (out);
++  idx = sb_skip_white (idx, in);
++
++  if (idx < in->len)
++    {
++      if (in->len > 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx]))
++      {
++        while (!ISSEP (in->ptr[idx]))
++          sb_add_char (out, in->ptr[idx++]);
++      }
++      else if (in->ptr[idx] == '%'
++             && alternate
++             && expand)
++      {
++        int val;
++        char buf[20];
++        /* Turns the next expression into a string.  */
++        /* xgettext: no-c-format */
++        idx = exp_get_abs (_("% operator needs absolute expression"),
++                           idx + 1,
++                           in,
++                           &val);
++        sprintf (buf, "%d", val);
++        sb_add_string (out, buf);
++      }
++      else if (in->ptr[idx] == '"'
++             || in->ptr[idx] == '<'
++             || (alternate && in->ptr[idx] == '\''))
++      {
++        if (alternate && expand)
++          {
++            /* Keep the quotes.  */
++            sb_add_char (out, '\"');
++
++            idx = getstring (idx, in, out);
++            sb_add_char (out, '\"');
++
++          }
++        else
++          {
++            idx = getstring (idx, in, out);
++          }
++      }
++      else
++      {
++        while (idx < in->len
++               && (in->ptr[idx] == '"'
++                   || in->ptr[idx] == '\''
++                   || pretend_quoted
++                   || !ISSEP (in->ptr[idx])))
++          {
++            if (in->ptr[idx] == '"'
++                || in->ptr[idx] == '\'')
++              {
++                char tchar = in->ptr[idx];
++                sb_add_char (out, in->ptr[idx++]);
++                while (idx < in->len
++                       && in->ptr[idx] != tchar)
++                  sb_add_char (out, in->ptr[idx++]);
++                if (idx == in->len)
++                  return idx;
++              }
++            sb_add_char (out, in->ptr[idx++]);
++          }
++      }
++    }
++
++  return idx;
++}
++
++/* Skip along sb in starting at idx, suck off whitespace a ( and more
++   whitespace.  Return the idx of the next char.  */
++
++static int
++skip_openp (idx, in)
++     int idx;
++     sb *in;
++{
++  idx = sb_skip_white (idx, in);
++  if (in->ptr[idx] != '(')
++    ERROR ((stderr, _("misplaced ( .\n")));
++  idx = sb_skip_white (idx + 1, in);
++  return idx;
++}
++
++/* Skip along sb in starting at idx, suck off whitespace a ) and more
++   whitespace.  Return the idx of the next char.  */
++
++static int
++skip_closep (idx, in)
++     int idx;
++     sb *in;
++{
++  idx = sb_skip_white (idx, in);
++  if (in->ptr[idx] != ')')
++    ERROR ((stderr, _("misplaced ).\n")));
++  idx = sb_skip_white (idx + 1, in);
++  return idx;
++}
++
++/* .len  */
++
++static int
++dolen (idx, in, out)
++     int idx;
++     sb *in;
++     sb *out;
++{
++
++  sb stringout;
++  char buffer[10];
++
++  sb_new (&stringout);
++  idx = skip_openp (idx, in);
++  idx = get_and_process (idx, in, &stringout);
++  idx = skip_closep (idx, in);
++  sprintf (buffer, "%d", stringout.len);
++  sb_add_string (out, buffer);
++
++  sb_kill (&stringout);
++  return idx;
++}
++
++/* .instr  */
++
++static int
++doinstr (idx, in, out)
++     int idx;
++     sb *in;
++     sb *out;
++{
++  sb string;
++  sb search;
++  int i;
++  int start;
++  int res;
++  char buffer[10];
++
++  sb_new (&string);
++  sb_new (&search);
++  idx = skip_openp (idx, in);
++  idx = get_and_process (idx, in, &string);
++  idx = sb_skip_comma (idx, in);
++  idx = get_and_process (idx, in, &search);
++  idx = sb_skip_comma (idx, in);
++  if (ISDIGIT (in->ptr[idx]))
++    {
++      idx = exp_get_abs (_(".instr needs absolute expresson.\n"), idx, in, &start);
++    }
++  else
++    {
++      start = 0;
++    }
++  idx = skip_closep (idx, in);
++  res = -1;
++  for (i = start; i < string.len; i++)
++    {
++      if (strncmp (string.ptr + i, search.ptr, search.len) == 0)
++      {
++        res = i;
++        break;
++      }
++    }
++  sprintf (buffer, "%d", res);
++  sb_add_string (out, buffer);
++  sb_kill (&string);
++  sb_kill (&search);
++  return idx;
++}
++
++static int
++dosubstr (idx, in, out)
++     int idx;
++     sb *in;
++     sb *out;
++{
++  sb string;
++  int pos;
++  int len;
++  sb_new (&string);
++
++  idx = skip_openp (idx, in);
++  idx = get_and_process (idx, in, &string);
++  idx = sb_skip_comma (idx, in);
++  idx = exp_get_abs (_("need absolute position.\n"), idx, in, &pos);
++  idx = sb_skip_comma (idx, in);
++  idx = exp_get_abs (_("need absolute length.\n"), idx, in, &len);
++  idx = skip_closep (idx, in);
++
++  if (len < 0 || pos < 0 ||
++      pos > string.len
++      || pos + len > string.len)
++    {
++      sb_add_string (out, " ");
++    }
++  else
++    {
++      sb_add_char (out, '"');
++      while (len > 0)
++      {
++        sb_add_char (out, string.ptr[pos++]);
++        len--;
++      }
++      sb_add_char (out, '"');
++    }
++  sb_kill (&string);
++  return idx;
++}
++
++/* Scan line, change tokens in the hash table to their replacements.  */
++
++static void
++process_assigns (idx, in, buf)
++     int idx;
++     sb *in;
++     sb *buf;
++{
++  while (idx < in->len)
++    {
++      hash_entry *ptr;
++      if (in->ptr[idx] == '\\'
++        && idx + 1 < in->len
++        && in->ptr[idx + 1] == '(')
++      {
++        do
++          {
++            sb_add_char (buf, in->ptr[idx]);
++            idx++;
++          }
++        while (idx < in->len && in->ptr[idx - 1] != ')');
++      }
++      else if (in->ptr[idx] == '\\'
++        && idx + 1 < in->len
++        && in->ptr[idx + 1] == '&')
++      {
++        idx = condass_lookup_name (in, idx + 2, buf, 1);
++      }
++      else if (in->ptr[idx] == '\\'
++             && idx + 1 < in->len
++             && in->ptr[idx + 1] == '$')
++      {
++        idx = condass_lookup_name (in, idx + 2, buf, 0);
++      }
++      else if (idx + 3 < in->len
++             && in->ptr[idx] == '.'
++             && TOUPPER (in->ptr[idx + 1]) == 'L'
++             && TOUPPER (in->ptr[idx + 2]) == 'E'
++             && TOUPPER (in->ptr[idx + 3]) == 'N')
++      idx = dolen (idx + 4, in, buf);
++      else if (idx + 6 < in->len
++             && in->ptr[idx] == '.'
++             && TOUPPER (in->ptr[idx + 1]) == 'I'
++             && TOUPPER (in->ptr[idx + 2]) == 'N'
++             && TOUPPER (in->ptr[idx + 3]) == 'S'
++             && TOUPPER (in->ptr[idx + 4]) == 'T'
++             && TOUPPER (in->ptr[idx + 5]) == 'R')
++      idx = doinstr (idx + 6, in, buf);
++      else if (idx + 7 < in->len
++             && in->ptr[idx] == '.'
++             && TOUPPER (in->ptr[idx + 1]) == 'S'
++             && TOUPPER (in->ptr[idx + 2]) == 'U'
++             && TOUPPER (in->ptr[idx + 3]) == 'B'
++             && TOUPPER (in->ptr[idx + 4]) == 'S'
++             && TOUPPER (in->ptr[idx + 5]) == 'T'
++             && TOUPPER (in->ptr[idx + 6]) == 'R')
++      idx = dosubstr (idx + 7, in, buf);
++      else if (ISFIRSTCHAR (in->ptr[idx]))
++      {
++        /* May be a simple name subsitution, see if we have a word.  */
++        sb acc;
++        int cur = idx + 1;
++        while (cur < in->len
++               && (ISNEXTCHAR (in->ptr[cur])))
++          cur++;
++
++        sb_new (&acc);
++        sb_add_buffer (&acc, in->ptr + idx, cur - idx);
++        ptr = hash_lookup (&assign_hash_table, &acc);
++        if (ptr)
++          {
++            /* Found a definition for it.  */
++            sb_add_sb (buf, &ptr->value.s);
++          }
++        else
++          {
++            /* No definition, just copy the word.  */
++            sb_add_sb (buf, &acc);
++          }
++        sb_kill (&acc);
++        idx = cur;
++      }
++      else
++      {
++        sb_add_char (buf, in->ptr[idx++]);
++      }
++    }
++}
++
++static int
++get_and_process (idx, in, out)
++     int idx;
++     sb *in;
++     sb *out;
++{
++  sb t;
++  sb_new (&t);
++  idx = get_any_string (idx, in, &t, 1, 0);
++  process_assigns (0, &t, out);
++  sb_kill (&t);
++  return idx;
++}
++
++static void
++process_file ()
++{
++  sb line;
++  sb t1, t2;
++  sb acc;
++  sb label_in;
++  int more;
++
++  sb_new (&line);
++  sb_new (&t1);
++  sb_new (&t2);
++  sb_new (&acc);
++  sb_new (&label_in);
++  sb_reset (&line);
++  more = get_line (&line);
++  while (more)
++    {
++      /* Find any label and pseudo op that we're intested in.  */
++      int l;
++      if (line.len == 0)
++      {
++        if (condass_on ())
++          fprintf (outfile, "\n");
++      }
++      else if (mri
++             && (line.ptr[0] == '*'
++                 || line.ptr[0] == '!'))
++      {
++        /* MRI line comment.  */
++        fprintf (outfile, "%s", sb_name (&line));
++      }
++      else
++      {
++        l = grab_label (&line, &label_in);
++        sb_reset (&label);
++
++        if (line.ptr[l] == ':')
++          l++;
++        while (ISWHITE (line.ptr[l]) && l < line.len)
++          l++;
++
++        if (label_in.len)
++          {
++            int do_assigns;
++
++            /* Munge the label, unless this is EQU or ASSIGN.  */
++            do_assigns = 1;
++            if (l < line.len
++                && (line.ptr[l] == '.' || alternate || mri))
++              {
++                int lx = l;
++
++                if (line.ptr[lx] == '.')
++                  ++lx;
++                if (lx + 3 <= line.len
++                    && strncasecmp ("EQU", line.ptr + lx, 3) == 0
++                    && (lx + 3 == line.len
++                        || ! ISFIRSTCHAR (line.ptr[lx + 3])))
++                  do_assigns = 0;
++                else if (lx + 6 <= line.len
++                         && strncasecmp ("ASSIGN", line.ptr + lx, 6) == 0
++                         && (lx + 6 == line.len
++                             || ! ISFIRSTCHAR (line.ptr[lx + 6])))
++                  do_assigns = 0;
++              }
++
++            if (do_assigns)
++              process_assigns (0, &label_in, &label);
++            else
++              sb_add_sb (&label, &label_in);
++          }
++
++        if (l < line.len)
++          {
++            if (process_pseudo_op (l, &line, &acc))
++              {
++
++              }
++            else if (condass_on ())
++              {
++                if (macro_op (l, &line))
++                  {
++
++                  }
++                else
++                  {
++                    {
++                      if (label.len)
++                        {
++                          fprintf (outfile, "%s:\t", sb_name (&label));
++                        }
++                      else
++                        fprintf (outfile, "\t");
++                      sb_reset (&t1);
++                      process_assigns (l, &line, &t1);
++                      sb_reset (&t2);
++                      change_base (0, &t1, &t2);
++                      fprintf (outfile, "%s\n", sb_name (&t2));
++                    }
++                  }
++              }
++          }
++        else
++          {
++            /* Only a label on this line.  */
++            if (label.len && condass_on ())
++              {
++                fprintf (outfile, "%s:\n", sb_name (&label));
++              }
++          }
++      }
++
++      if (had_end)
++      break;
++      sb_reset (&line);
++      more = get_line (&line);
++    }
++
++  if (!had_end && !mri)
++    WARNING ((stderr, _("END missing from end of file.\n")));
++}
++
++static void
++free_old_entry (ptr)
++     hash_entry *ptr;
++{
++  if (ptr)
++    {
++      if (ptr->type == hash_string)
++      sb_kill (&ptr->value.s);
++    }
++}
++
++/* name: .ASSIGNA <value>  */
++
++static void
++do_assigna (idx, in)
++     int idx;
++     sb *in;
++{
++  sb tmp;
++  int val;
++  sb_new (&tmp);
++
++  process_assigns (idx, in, &tmp);
++  idx = exp_get_abs (_(".ASSIGNA needs constant expression argument.\n"), 0, &tmp, &val);
++
++  if (!label.len)
++    {
++      ERROR ((stderr, _(".ASSIGNA without label.\n")));
++    }
++  else
++    {
++      hash_entry *ptr = hash_create (&vars, &label);
++      free_old_entry (ptr);
++      ptr->type = hash_integer;
++      ptr->value.i = val;
++    }
++  sb_kill (&tmp);
++}
++
++/* name: .ASSIGNC <string>  */
++
++static void
++do_assignc (idx, in)
++     int idx;
++     sb *in;
++{
++  sb acc;
++  sb_new (&acc);
++  idx = getstring (idx, in, &acc);
++
++  if (!label.len)
++    {
++      ERROR ((stderr, _(".ASSIGNS without label.\n")));
++    }
++  else
++    {
++      hash_entry *ptr = hash_create (&vars, &label);
++      free_old_entry (ptr);
++      ptr->type = hash_string;
++      sb_new (&ptr->value.s);
++      sb_add_sb (&ptr->value.s, &acc);
++    }
++  sb_kill (&acc);
++}
++
++/* name: .REG (reg)  */
++
++static void
++do_reg (idx, in)
++     int idx;
++     sb *in;
++{
++  /* Remove reg stuff from inside parens.  */
++  sb what;
++  if (!mri)
++    idx = skip_openp (idx, in);
++  else
++    idx = sb_skip_white (idx, in);
++  sb_new (&what);
++  while (idx < in->len
++       && (mri
++           ? ! eol (idx, in)
++           : in->ptr[idx] != ')'))
++    {
++      sb_add_char (&what, in->ptr[idx]);
++      idx++;
++    }
++  hash_add_to_string_table (&assign_hash_table, &label, &what, 1);
++  sb_kill (&what);
++}
++
++static int
++condass_lookup_name (inbuf, idx, out, warn)
++     sb *inbuf;
++     int idx;
++     sb *out;
++     int warn;
++{
++  hash_entry *ptr;
++  sb condass_acc;
++  sb_new (&condass_acc);
++
++  while (idx < inbuf->len
++       && ISNEXTCHAR (inbuf->ptr[idx]))
++    {
++      sb_add_char (&condass_acc, inbuf->ptr[idx++]);
++    }
++
++  if (inbuf->ptr[idx] == '\'')
++    idx++;
++  ptr = hash_lookup (&vars, &condass_acc);
++
++  if (!ptr)
++    {
++      if (warn)
++      {
++        WARNING ((stderr, _("Can't find preprocessor variable %s.\n"), sb_name (&condass_acc)));
++      }
++      else
++      {
++        sb_add_string (out, "0");
++      }
++    }
++  else
++    {
++      if (ptr->type == hash_integer)
++      {
++        char buffer[30];
++        sprintf (buffer, "%d", ptr->value.i);
++        sb_add_string (out, buffer);
++      }
++      else
++      {
++        sb_add_sb (out, &ptr->value.s);
++      }
++    }
++  sb_kill (&condass_acc);
++  return idx;
++}
++
++#define EQ 1
++#define NE 2
++#define GE 3
++#define LT 4
++#define LE 5
++#define GT 6
++#define NEVER 7
++
++static int
++whatcond (idx, in, val)
++     int idx;
++     sb *in;
++     int *val;
++{
++  int cond;
++
++  idx = sb_skip_white (idx, in);
++  cond = NEVER;
++  if (idx + 1 < in->len)
++    {
++      char *p;
++      char a, b;
++
++      p = in->ptr + idx;
++      a = TOUPPER (p[0]);
++      b = TOUPPER (p[1]);
++      if (a == 'E' && b == 'Q')
++      cond = EQ;
++      else if (a == 'N' && b == 'E')
++      cond = NE;
++      else if (a == 'L' && b == 'T')
++      cond = LT;
++      else if (a == 'L' && b == 'E')
++      cond = LE;
++      else if (a == 'G' && b == 'T')
++      cond = GT;
++      else if (a == 'G' && b == 'E')
++      cond = GE;
++    }
++  if (cond == NEVER)
++    {
++      ERROR ((stderr, _("Comparison operator must be one of EQ, NE, LT, LE, GT or GE.\n")));
++      cond = NEVER;
++    }
++  idx = sb_skip_white (idx + 2, in);
++  *val = cond;
++  return idx;
++}
++
++static int
++istrue (idx, in)
++     int idx;
++     sb *in;
++{
++  int res;
++  sb acc_a;
++  sb cond;
++  sb acc_b;
++  sb_new (&acc_a);
++  sb_new (&cond);
++  sb_new (&acc_b);
++  idx = sb_skip_white (idx, in);
++
++  if (in->ptr[idx] == '"')
++    {
++      int cond;
++      int same;
++      /* This is a string comparision.  */
++      idx = getstring (idx, in, &acc_a);
++      idx = whatcond (idx, in, &cond);
++      idx = getstring (idx, in, &acc_b);
++      same = acc_a.len == acc_b.len
++      && (strncmp (acc_a.ptr, acc_b.ptr, acc_a.len) == 0);
++
++      if (cond != EQ && cond != NE)
++      {
++        ERROR ((stderr, _("Comparison operator for strings must be EQ or NE\n")));
++        res = 0;
++      }
++      else
++      res = (cond != EQ) ^ same;
++    }
++  else
++    /* This is a numeric expression.  */
++    {
++      int vala;
++      int valb;
++      int cond;
++      idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), idx, in, &vala);
++      idx = whatcond (idx, in, &cond);
++      idx = sb_skip_white (idx, in);
++      if (in->ptr[idx] == '"')
++      {
++        WARNING ((stderr, _("String compared against expression.\n")));
++        res = 0;
++      }
++      else
++      {
++        idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"), idx, in, &valb);
++        switch (cond)
++          {
++          default:
++            res = 42;
++            break;
++          case EQ:
++            res = vala == valb;
++            break;
++          case NE:
++            res = vala != valb;
++            break;
++          case LT:
++            res = vala < valb;
++            break;
++          case LE:
++            res = vala <= valb;
++            break;
++          case GT:
++            res = vala > valb;
++            break;
++          case GE:
++            res = vala >= valb;
++            break;
++          case NEVER:
++            res = 0;
++            break;
++          }
++      }
++    }
++
++  sb_kill (&acc_a);
++  sb_kill (&cond);
++  sb_kill (&acc_b);
++  return res;
++}
++
++/* .AIF  */
++
++static void
++do_aif (idx, in)
++     int idx;
++     sb *in;
++{
++  if (ifi >= IFNESTING)
++    {
++      FATAL ((stderr, _("AIF nesting unreasonable.\n")));
++    }
++  ifi++;
++  ifstack[ifi].on = ifstack[ifi - 1].on ? istrue (idx, in) : 0;
++  ifstack[ifi].hadelse = 0;
++}
++
++/* .AELSE  */
++
++static void
++do_aelse ()
++{
++  ifstack[ifi].on = ifstack[ifi - 1].on ? !ifstack[ifi].on : 0;
++  if (ifstack[ifi].hadelse)
++    {
++      ERROR ((stderr, _("Multiple AELSEs in AIF.\n")));
++    }
++  ifstack[ifi].hadelse = 1;
++}
++
++/* .AENDI  */
++
++static void
++do_aendi ()
++{
++  if (ifi != 0)
++    {
++      ifi--;
++    }
++  else
++    {
++      ERROR ((stderr, _("AENDI without AIF.\n")));
++    }
++}
++
++static int
++condass_on ()
++{
++  return ifstack[ifi].on;
++}
++
++/* MRI IFEQ, IFNE, IFLT, IFLE, IFGE, IFGT.  */
++
++static void
++do_if (idx, in, cond)
++     int idx;
++     sb *in;
++     int cond;
++{
++  int val;
++  int res;
++
++  if (ifi >= IFNESTING)
++    {
++      FATAL ((stderr, _("IF nesting unreasonable.\n")));
++    }
++
++  idx = exp_get_abs (_("Conditional operator must have absolute operands.\n"),
++                   idx, in, &val);
++  switch (cond)
++    {
++    default:
++    case EQ: res = val == 0; break;
++    case NE: res = val != 0; break;
++    case LT: res = val <  0; break;
++    case LE: res = val <= 0; break;
++    case GE: res = val >= 0; break;
++    case GT: res = val >  0; break;
++    }
++
++  ifi++;
++  ifstack[ifi].on = ifstack[ifi - 1].on ? res : 0;
++  ifstack[ifi].hadelse = 0;
++}
++
++/* Get a string for the MRI IFC or IFNC pseudo-ops.  */
++
++static int
++get_mri_string (idx, in, val, terminator)
++     int idx;
++     sb *in;
++     sb *val;
++     int terminator;
++{
++  idx = sb_skip_white (idx, in);
++
++  if (idx < in->len
++      && in->ptr[idx] == '\'')
++    {
++      sb_add_char (val, '\'');
++      for (++idx; idx < in->len; ++idx)
++      {
++        sb_add_char (val, in->ptr[idx]);
++        if (in->ptr[idx] == '\'')
++          {
++            ++idx;
++            if (idx >= in->len
++                || in->ptr[idx] != '\'')
++              break;
++          }
++      }
++      idx = sb_skip_white (idx, in);
++    }
++  else
++    {
++      int i;
++
++      while (idx < in->len
++           && in->ptr[idx] != terminator)
++      {
++        sb_add_char (val, in->ptr[idx]);
++        ++idx;
++      }
++      i = val->len - 1;
++      while (i >= 0 && ISWHITE (val->ptr[i]))
++      --i;
++      val->len = i + 1;
++    }
++
++  return idx;
++}
++
++/* MRI IFC, IFNC  */
++
++static void
++do_ifc (idx, in, ifnc)
++     int idx;
++     sb *in;
++     int ifnc;
++{
++  sb first;
++  sb second;
++  int res;
++
++  if (ifi >= IFNESTING)
++    {
++      FATAL ((stderr, _("IF nesting unreasonable.\n")));
++    }
++
++  sb_new (&first);
++  sb_new (&second);
++
++  idx = get_mri_string (idx, in, &first, ',');
++
++  if (idx >= in->len || in->ptr[idx] != ',')
++    {
++      ERROR ((stderr, _("Bad format for IF or IFNC.\n")));
++      return;
++    }
++
++  idx = get_mri_string (idx + 1, in, &second, ';');
++
++  res = (first.len == second.len
++       && strncmp (first.ptr, second.ptr, first.len) == 0);
++  res ^= ifnc;
++
++  ifi++;
++  ifstack[ifi].on = ifstack[ifi - 1].on ? res : 0;
++  ifstack[ifi].hadelse = 0;
++}
++
++/* .ENDR  */
++
++static void
++do_aendr ()
++{
++  if (!mri)
++    ERROR ((stderr, _("AENDR without a AREPEAT.\n")));
++  else
++    ERROR ((stderr, _("ENDR without a REPT.\n")));
++}
++
++/* .AWHILE  */
++
++static void
++do_awhile (idx, in)
++     int idx;
++     sb *in;
++{
++  int line = linecount ();
++  sb exp;
++  sb sub;
++  int doit;
++
++  sb_new (&sub);
++  sb_new (&exp);
++
++  process_assigns (idx, in, &exp);
++  doit = istrue (0, &exp);
++
++  if (! buffer_and_nest ("AWHILE", "AENDW", &sub, get_line))
++    FATAL ((stderr, _("AWHILE without a AENDW at %d.\n"), line - 1));
++
++  /* Turn
++      .AWHILE exp
++           foo
++      .AENDW
++     into
++        foo
++      .AWHILE exp
++      foo
++      .ENDW
++  */
++
++  if (doit)
++    {
++      int index = include_next_index ();
++
++      sb copy;
++      sb_new (&copy);
++      sb_add_sb (&copy, &sub);
++      sb_add_sb (&copy, in);
++      sb_add_string (&copy, "\n");
++      sb_add_sb (&copy, &sub);
++      sb_add_string (&copy, "\t.AENDW\n");
++      /* Push another WHILE.  */
++      include_buf (&exp, &copy, include_while, index);
++      sb_kill (&copy);
++    }
++  sb_kill (&exp);
++  sb_kill (&sub);
++}
++
++/* .AENDW  */
++
++static void
++do_aendw ()
++{
++  ERROR ((stderr, _("AENDW without a AENDW.\n")));
++}
++
++/* .EXITM
++
++   Pop things off the include stack until the type and index changes.  */
++
++static void
++do_exitm ()
++{
++  include_type type = sp->type;
++  if (type == include_repeat
++      || type == include_while
++      || type == include_macro)
++    {
++      int index = sp->index;
++      include_pop ();
++      while (sp->index == index
++           && sp->type == type)
++      {
++        include_pop ();
++      }
++    }
++}
++
++/* .AREPEAT  */
++
++static void
++do_arepeat (idx, in)
++     int idx;
++     sb *in;
++{
++  int line = linecount ();
++  sb exp;                     /* Buffer with expression in it.  */
++  sb copy;                    /* Expanded repeat block.  */
++  sb sub;                     /* Contents of AREPEAT.  */
++  int rc;
++  int ret;
++  char buffer[30];
++
++  sb_new (&exp);
++  sb_new (&copy);
++  sb_new (&sub);
++  process_assigns (idx, in, &exp);
++  idx = exp_get_abs (_("AREPEAT must have absolute operand.\n"), 0, &exp, &rc);
++  if (!mri)
++    ret = buffer_and_nest ("AREPEAT", "AENDR", &sub, get_line);
++  else
++    ret = buffer_and_nest ("REPT", "ENDR", &sub, get_line);
++  if (! ret)
++    FATAL ((stderr, _("AREPEAT without a AENDR at %d.\n"), line - 1));
++  if (rc > 0)
++    {
++      /* Push back the text following the repeat, and another repeat block
++       so
++       .AREPEAT 20
++       foo
++       .AENDR
++       gets turned into
++       foo
++       .AREPEAT 19
++       foo
++       .AENDR
++      */
++      int index = include_next_index ();
++      sb_add_sb (&copy, &sub);
++      if (rc > 1)
++      {
++        if (!mri)
++          sprintf (buffer, "\t.AREPEAT        %d\n", rc - 1);
++        else
++          sprintf (buffer, "\tREPT    %d\n", rc - 1);
++        sb_add_string (&copy, buffer);
++        sb_add_sb (&copy, &sub);
++        if (!mri)
++          sb_add_string (&copy, "     .AENDR\n");
++        else
++          sb_add_string (&copy, "     ENDR\n");
++      }
++
++      include_buf (&exp, &copy, include_repeat, index);
++    }
++  sb_kill (&exp);
++  sb_kill (&sub);
++  sb_kill (&copy);
++}
++
++/* .ENDM  */
++
++static void
++do_endm ()
++{
++  ERROR ((stderr, _(".ENDM without a matching .MACRO.\n")));
++}
++
++/* MRI IRP pseudo-op.  */
++
++static void
++do_irp (idx, in, irpc)
++     int idx;
++     sb *in;
++     int irpc;
++{
++  const char *err;
++  sb out;
++
++  sb_new (&out);
++
++  err = expand_irp (irpc, idx, in, &out, get_line, comment_char);
++  if (err != NULL)
++    ERROR ((stderr, "%s\n", err));
++
++  fprintf (outfile, "%s", sb_terminate (&out));
++
++  sb_kill (&out);
++}
++
++/* Macro processing.  */
++
++/* Parse off LOCAL n1, n2,... Invent a label name for it.  */
++
++static void
++do_local (idx, line)
++     int idx ATTRIBUTE_UNUSED;
++     sb *line ATTRIBUTE_UNUSED;
++{
++  ERROR ((stderr, _("LOCAL outside of MACRO")));
++}
++
++static void
++do_macro (idx, in)
++     int idx;
++     sb *in;
++{
++  const char *err;
++  int line = linecount ();
++
++  err = define_macro (idx, in, &label, get_line, "[gasp]", line, (const char **) NULL);
++  if (err != NULL)
++    ERROR ((stderr, _("macro at line %d: %s\n"), line - 1, err));
++}
++
++static int
++macro_op (idx, in)
++     int idx;
++     sb *in;
++{
++  const char *err;
++  sb out;
++  sb name;
++
++  if (! macro_defined)
++    return 0;
++
++  sb_terminate (in);
++  if (! check_macro (in->ptr + idx, &out, comment_char, &err, NULL))
++    return 0;
++
++  if (err != NULL)
++    ERROR ((stderr, "%s\n", err));
++
++  sb_new (&name);
++  sb_add_string (&name, _("macro expansion"));
++
++  include_buf (&name, &out, include_macro, include_next_index ());
++
++  sb_kill (&name);
++  sb_kill (&out);
++
++  return 1;
++}
++
++/* String handling.  */
++
++static int
++getstring (idx, in, acc)
++     int idx;
++     sb *in;
++     sb *acc;
++{
++  idx = sb_skip_white (idx, in);
++
++  while (idx < in->len
++       && (in->ptr[idx] == '"'
++           || in->ptr[idx] == '<'
++           || (in->ptr[idx] == '\'' && alternate)))
++    {
++      if (in->ptr[idx] == '<')
++      {
++        if (alternate || mri)
++          {
++            int nest = 0;
++            idx++;
++            while ((in->ptr[idx] != '>' || nest)
++                   && idx < in->len)
++              {
++                if (in->ptr[idx] == '!')
++                  {
++                    idx++;
++                    sb_add_char (acc, in->ptr[idx++]);
++                  }
++                else
++                  {
++                    if (in->ptr[idx] == '>')
++                      nest--;
++                    if (in->ptr[idx] == '<')
++                      nest++;
++                    sb_add_char (acc, in->ptr[idx++]);
++                  }
++              }
++            idx++;
++          }
++        else
++          {
++            int code;
++            idx++;
++            idx = exp_get_abs (_("Character code in string must be absolute expression.\n"),
++                               idx, in, &code);
++            sb_add_char (acc, code);
++
++            if (in->ptr[idx] != '>')
++              ERROR ((stderr, _("Missing > for character code.\n")));
++            idx++;
++          }
++      }
++      else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'')
++      {
++        char tchar = in->ptr[idx];
++        idx++;
++        while (idx < in->len)
++          {
++            if (alternate && in->ptr[idx] == '!')
++              {
++                idx++;
++                sb_add_char (acc, in->ptr[idx++]);
++              }
++            else
++              {
++                if (in->ptr[idx] == tchar)
++                  {
++                    idx++;
++                    if (idx >= in->len || in->ptr[idx] != tchar)
++                      break;
++                  }
++                sb_add_char (acc, in->ptr[idx]);
++                idx++;
++              }
++          }
++      }
++    }
++
++  return idx;
++}
++
++/* .SDATA[C|Z] <string>  */
++
++static void
++do_sdata (idx, in, type)
++     int idx;
++     sb *in;
++     int type;
++{
++  int nc = 0;
++  int pidx = -1;
++  sb acc;
++  sb_new (&acc);
++  fprintf (outfile, ".byte\t");
++
++  while (!eol (idx, in))
++    {
++      int i;
++      sb_reset (&acc);
++      idx = sb_skip_white (idx, in);
++      while (!eol (idx, in))
++      {
++        pidx = idx = get_any_string (idx, in, &acc, 0, 1);
++        if (type == 'c')
++          {
++            if (acc.len > 255)
++              {
++                ERROR ((stderr, _("string for SDATAC longer than 255 characters (%d).\n"), acc.len));
++              }
++            fprintf (outfile, "%d", acc.len);
++            nc = 1;
++          }
++
++        for (i = 0; i < acc.len; i++)
++          {
++            if (nc)
++              {
++                fprintf (outfile, ",");
++              }
++            fprintf (outfile, "%d", acc.ptr[i]);
++            nc = 1;
++          }
++
++        if (type == 'z')
++          {
++            if (nc)
++              fprintf (outfile, ",");
++            fprintf (outfile, "0");
++          }
++        idx = sb_skip_comma (idx, in);
++        if (idx == pidx)
++          break;
++      }
++      if (!alternate && in->ptr[idx] != ',' && idx != in->len)
++      {
++        fprintf (outfile, "\n");
++        ERROR ((stderr, _("illegal character in SDATA line (0x%x).\n"),
++                in->ptr[idx]));
++        break;
++      }
++      idx++;
++    }
++  sb_kill (&acc);
++  fprintf (outfile, "\n");
++}
++
++/* .SDATAB <count> <string>  */
++
++static void
++do_sdatab (idx, in)
++     int idx;
++     sb *in;
++{
++  int repeat;
++  int i;
++  sb acc;
++  sb_new (&acc);
++
++  idx = exp_get_abs (_("Must have absolute SDATAB repeat count.\n"), idx, in, &repeat);
++  if (repeat <= 0)
++    {
++      ERROR ((stderr, _("Must have positive SDATAB repeat count (%d).\n"), repeat));
++      repeat = 1;
++    }
++
++  idx = sb_skip_comma (idx, in);
++  idx = getstring (idx, in, &acc);
++
++  for (i = 0; i < repeat; i++)
++    {
++      if (i)
++      fprintf (outfile, "\t");
++      fprintf (outfile, ".byte\t");
++      sb_print (outfile, &acc);
++      fprintf (outfile, "\n");
++    }
++  sb_kill (&acc);
++
++}
++
++static int
++new_file (name)
++     const char *name;
++{
++  FILE *newone = fopen (name, "r");
++  if (!newone)
++    return 0;
++
++  if (isp == MAX_INCLUDES)
++    FATAL ((stderr, _("Unreasonable include depth (%ld).\n"), (long) isp));
++
++  sp++;
++  sp->handle = newone;
++
++  sb_new (&sp->name);
++  sb_add_string (&sp->name, name);
++
++  sp->linecount = 1;
++  sp->pushback_index = 0;
++  sp->type = include_file;
++  sp->index = 0;
++  sb_new (&sp->pushback);
++  return 1;
++}
++
++static void
++do_include (idx, in)
++     int idx;
++     sb *in;
++{
++  sb t;
++  sb cat;
++  include_path *includes;
++
++  sb_new (&t);
++  sb_new (&cat);
++
++  if (! mri)
++    idx = getstring (idx, in, &t);
++  else
++    {
++      idx = sb_skip_white (idx, in);
++      while (idx < in->len && ! ISWHITE (in->ptr[idx]))
++      {
++        sb_add_char (&t, in->ptr[idx]);
++        ++idx;
++      }
++    }
++
++  for (includes = paths_head; includes; includes = includes->next)
++    {
++      sb_reset (&cat);
++      sb_add_sb (&cat, &includes->path);
++      sb_add_char (&cat, '/');
++      sb_add_sb (&cat, &t);
++      if (new_file (sb_name (&cat)))
++      {
++        break;
++      }
++    }
++  if (!includes)
++    {
++      if (! new_file (sb_name (&t)))
++      FATAL ((stderr, _("Can't open include file `%s'.\n"), sb_name (&t)));
++    }
++  sb_kill (&cat);
++  sb_kill (&t);
++}
++
++static void
++include_pop ()
++{
++  if (sp != include_stack)
++    {
++      if (sp->handle)
++      fclose (sp->handle);
++      sp--;
++    }
++}
++
++/* Get the next character from the include stack.  If there's anything
++   in the pushback buffer, take that first.  If we're at eof, pop from
++   the stack and try again.  Keep the linecount up to date.  */
++
++static int
++get ()
++{
++  int r;
++
++  if (sp->pushback.len != sp->pushback_index)
++    {
++      r = (char) (sp->pushback.ptr[sp->pushback_index++]);
++      /* When they've all gone, reset the pointer.  */
++      if (sp->pushback_index == sp->pushback.len)
++      {
++        sp->pushback.len = 0;
++        sp->pushback_index = 0;
++      }
++    }
++  else if (sp->handle)
++    {
++      r = getc (sp->handle);
++    }
++  else
++    r = EOF;
++
++  if (r == EOF && isp)
++    {
++      include_pop ();
++      r = get ();
++      while (r == EOF && isp)
++      {
++        include_pop ();
++        r = get ();
++      }
++      return r;
++    }
++  if (r == '\n')
++    {
++      sp->linecount++;
++    }
++
++  return r;
++}
++
++static int
++linecount ()
++{
++  return sp->linecount;
++}
++
++static int
++include_next_index ()
++{
++  static int index;
++  if (!unreasonable
++      && index > MAX_REASONABLE)
++    FATAL ((stderr, _("Unreasonable expansion (-u turns off check).\n")));
++  return ++index;
++}
++
++/* Initialize the chartype vector.  */
++
++static void
++chartype_init ()
++{
++  int x;
++  for (x = 0; x < 256; x++)
++    {
++      if (ISALPHA (x) || x == '_' || x == '$')
++      chartype[x] |= FIRSTBIT;
++
++      if (mri && x == '.')
++      chartype[x] |= FIRSTBIT;
++
++      if (ISDIGIT (x) || ISALPHA (x) || x == '_' || x == '$')
++      chartype[x] |= NEXTBIT;
++
++      if (x == ' ' || x == '\t' || x == ',' || x == '"' || x == ';'
++        || x == '"' || x == '<' || x == '>' || x == ')' || x == '(')
++      chartype[x] |= SEPBIT;
++
++      if (x == 'b' || x == 'B'
++        || x == 'q' || x == 'Q'
++        || x == 'h' || x == 'H'
++        || x == 'd' || x == 'D')
++      chartype [x] |= BASEBIT;
++
++      if (x == ' ' || x == '\t')
++      chartype[x] |= WHITEBIT;
++
++      if (x == comment_char)
++      chartype[x] |= COMMENTBIT;
++    }
++}
++
++/* What to do with all the keywords.  */
++#define PROCESS       0x1000  /* Run substitution over the line.  */
++#define LAB           0x2000  /* Spit out the label.  */
++
++#define K_EQU                 (PROCESS|1)
++#define K_ASSIGN      (PROCESS|2)
++#define K_REG                 (PROCESS|3)
++#define K_ORG                 (PROCESS|4)
++#define K_RADIX       (PROCESS|5)
++#define K_DATA                (LAB|PROCESS|6)
++#define K_DATAB       (LAB|PROCESS|7)
++#define K_SDATA       (LAB|PROCESS|8)
++#define K_SDATAB      (LAB|PROCESS|9)
++#define K_SDATAC      (LAB|PROCESS|10)
++#define K_SDATAZ      (LAB|PROCESS|11)
++#define K_RES                 (LAB|PROCESS|12)
++#define K_SRES                (LAB|PROCESS|13)
++#define K_SRESC       (LAB|PROCESS|14)
++#define K_SRESZ       (LAB|PROCESS|15)
++#define K_EXPORT      (LAB|PROCESS|16)
++#define K_GLOBAL      (LAB|PROCESS|17)
++#define K_PRINT       (LAB|PROCESS|19)
++#define K_FORM                (LAB|PROCESS|20)
++#define K_HEADING     (LAB|PROCESS|21)
++#define K_PAGE                (LAB|PROCESS|22)
++#define K_IMPORT      (LAB|PROCESS|23)
++#define K_PROGRAM     (LAB|PROCESS|24)
++#define K_END         (PROCESS|25)
++#define K_INCLUDE     (PROCESS|26)
++#define K_IGNORED     (PROCESS|27)
++#define K_ASSIGNA     (PROCESS|28)
++#define K_ASSIGNC     (29)
++#define K_AIF         (PROCESS|30)
++#define K_AELSE               (PROCESS|31)
++#define K_AENDI               (PROCESS|32)
++#define K_AREPEAT     (PROCESS|33)
++#define K_AENDR               (PROCESS|34)
++#define K_AWHILE      (35)
++#define K_AENDW               (PROCESS|36)
++#define K_EXITM               (37)
++#define K_MACRO               (PROCESS|38)
++#define K_ENDM                (39)
++#define K_ALIGN               (PROCESS|LAB|40)
++#define K_ALTERNATE     (41)
++#define K_DB          (LAB|PROCESS|42)
++#define K_DW          (LAB|PROCESS|43)
++#define K_DL          (LAB|PROCESS|44)
++#define K_LOCAL               (45)
++#define K_IFEQ                (PROCESS|46)
++#define K_IFNE                (PROCESS|47)
++#define K_IFLT                (PROCESS|48)
++#define K_IFLE                (PROCESS|49)
++#define K_IFGE                (PROCESS|50)
++#define K_IFGT                (PROCESS|51)
++#define K_IFC         (PROCESS|52)
++#define K_IFNC                (PROCESS|53)
++#define K_IRP         (PROCESS|54)
++#define K_IRPC                (PROCESS|55)
++
++struct keyword {
++  char *name;
++  int code;
++  int extra;
++};
++
++static struct keyword kinfo[] = {
++  { "EQU", K_EQU, 0 },
++  { "ALTERNATE", K_ALTERNATE, 0 },
++  { "ASSIGN", K_ASSIGN, 0 },
++  { "REG", K_REG, 0 },
++  { "ORG", K_ORG, 0 },
++  { "RADIX", K_RADIX, 0 },
++  { "DATA", K_DATA, 0 },
++  { "DB", K_DB, 0 },
++  { "DW", K_DW, 0 },
++  { "DL", K_DL, 0 },
++  { "DATAB", K_DATAB, 0 },
++  { "SDATA", K_SDATA, 0 },
++  { "SDATAB", K_SDATAB, 0 },
++  { "SDATAZ", K_SDATAZ, 0 },
++  { "SDATAC", K_SDATAC, 0 },
++  { "RES", K_RES, 0 },
++  { "SRES", K_SRES, 0 },
++  { "SRESC", K_SRESC, 0 },
++  { "SRESZ", K_SRESZ, 0 },
++  { "EXPORT", K_EXPORT, 0 },
++  { "GLOBAL", K_GLOBAL, 0 },
++  { "PRINT", K_PRINT, 0 },
++  { "FORM", K_FORM, 0 },
++  { "HEADING", K_HEADING, 0 },
++  { "PAGE", K_PAGE, 0 },
++  { "PROGRAM", K_IGNORED, 0 },
++  { "END", K_END, 0 },
++  { "INCLUDE", K_INCLUDE, 0 },
++  { "ASSIGNA", K_ASSIGNA, 0 },
++  { "ASSIGNC", K_ASSIGNC, 0 },
++  { "AIF", K_AIF, 0 },
++  { "AELSE", K_AELSE, 0 },
++  { "AENDI", K_AENDI, 0 },
++  { "AREPEAT", K_AREPEAT, 0 },
++  { "AENDR", K_AENDR, 0 },
++  { "EXITM", K_EXITM, 0 },
++  { "MACRO", K_MACRO, 0 },
++  { "ENDM", K_ENDM, 0 },
++  { "AWHILE", K_AWHILE, 0 },
++  { "ALIGN", K_ALIGN, 0 },
++  { "AENDW", K_AENDW, 0 },
++  { "ALTERNATE", K_ALTERNATE, 0 },
++  { "LOCAL", K_LOCAL, 0 },
++  { NULL, 0, 0 }
++};
++
++/* Although the conditional operators are handled by gas, we need to
++   handle them here as well, in case they are used in a recursive
++   macro to end the recursion.  */
++
++static struct keyword mrikinfo[] = {
++  { "IFEQ", K_IFEQ, 0 },
++  { "IFNE", K_IFNE, 0 },
++  { "IFLT", K_IFLT, 0 },
++  { "IFLE", K_IFLE, 0 },
++  { "IFGE", K_IFGE, 0 },
++  { "IFGT", K_IFGT, 0 },
++  { "IFC", K_IFC, 0 },
++  { "IFNC", K_IFNC, 0 },
++  { "ELSEC", K_AELSE, 0 },
++  { "ENDC", K_AENDI, 0 },
++  { "MEXIT", K_EXITM, 0 },
++  { "REPT", K_AREPEAT, 0 },
++  { "IRP", K_IRP, 0 },
++  { "IRPC", K_IRPC, 0 },
++  { "ENDR", K_AENDR, 0 },
++  { NULL, 0, 0 }
++};
++
++/* Look for a pseudo op on the line. If one's there then call
++   its handler.  */
++
++static int
++process_pseudo_op (idx, line, acc)
++     int idx;
++     sb *line;
++     sb *acc;
++{
++  int oidx = idx;
++
++  if (line->ptr[idx] == '.' || alternate || mri)
++    {
++      /* Scan forward and find pseudo name.  */
++      char *in;
++      hash_entry *ptr;
++
++      char *s;
++      char *e;
++      if (line->ptr[idx] == '.')
++      idx++;
++      in = line->ptr + idx;
++      s = in;
++      e = s;
++      sb_reset (acc);
++
++      while (idx < line->len && *e && ISFIRSTCHAR (*e))
++      {
++        sb_add_char (acc, *e);
++        e++;
++        idx++;
++      }
++
++      ptr = hash_lookup (&keyword_hash_table, acc);
++
++      if (!ptr)
++      {
++#if 0
++        /* This one causes lots of pain when trying to preprocess
++           ordinary code.  */
++        WARNING ((stderr, _("Unrecognised pseudo op `%s'.\n"),
++                  sb_name (acc)));
++#endif
++        return 0;
++      }
++      if (ptr->value.i & LAB)
++      {
++        /* Output the label.  */
++        if (label.len)
++          {
++            fprintf (outfile, "%s:\t", sb_name (&label));
++          }
++        else
++          fprintf (outfile, "\t");
++      }
++
++      if (mri && ptr->value.i == K_END)
++      {
++        sb t;
++
++        sb_new (&t);
++        sb_add_buffer (&t, line->ptr + oidx, idx - oidx);
++        fprintf (outfile, "\t%s", sb_name (&t));
++        sb_kill (&t);
++      }
++
++      if (ptr->value.i & PROCESS)
++      {
++        /* Polish the rest of the line before handling the pseudo op.  */
++#if 0
++        strip_comments (line);
++#endif
++        sb_reset (acc);
++        process_assigns (idx, line, acc);
++        sb_reset (line);
++        change_base (0, acc, line);
++        idx = 0;
++      }
++      if (!condass_on ())
++      {
++        switch (ptr->value.i)
++          {
++          case K_AIF:
++            do_aif (idx, line);
++            break;
++          case K_AELSE:
++            do_aelse ();
++            break;
++          case K_AENDI:
++            do_aendi ();
++            break;
++          }
++        return 1;
++      }
++      else
++      {
++        switch (ptr->value.i)
++          {
++          case K_ALTERNATE:
++            alternate = 1;
++            macro_init (1, mri, 0, exp_get_abs);
++            return 1;
++          case K_AELSE:
++            do_aelse ();
++            return 1;
++          case K_AENDI:
++            do_aendi ();
++            return 1;
++          case K_ORG:
++            ERROR ((stderr, _("ORG command not allowed.\n")));
++            break;
++          case K_RADIX:
++            do_radix (line);
++            return 1;
++          case K_DB:
++            do_data (idx, line, 1);
++            return 1;
++          case K_DW:
++            do_data (idx, line, 2);
++            return 1;
++          case K_DL:
++            do_data (idx, line, 4);
++            return 1;
++          case K_DATA:
++            do_data (idx, line, 0);
++            return 1;
++          case K_DATAB:
++            do_datab (idx, line);
++            return 1;
++          case K_SDATA:
++            do_sdata (idx, line, 0);
++            return 1;
++          case K_SDATAB:
++            do_sdatab (idx, line);
++            return 1;
++          case K_SDATAC:
++            do_sdata (idx, line, 'c');
++            return 1;
++          case K_SDATAZ:
++            do_sdata (idx, line, 'z');
++            return 1;
++          case K_ASSIGN:
++            do_assign (0, 0, line);
++            return 1;
++          case K_AIF:
++            do_aif (idx, line);
++            return 1;
++          case K_AREPEAT:
++            do_arepeat (idx, line);
++            return 1;
++          case K_AENDW:
++            do_aendw ();
++            return 1;
++          case K_AWHILE:
++            do_awhile (idx, line);
++            return 1;
++          case K_AENDR:
++            do_aendr ();
++            return 1;
++          case K_EQU:
++            do_assign (1, idx, line);
++            return 1;
++          case K_ALIGN:
++            do_align (idx, line);
++            return 1;
++          case K_RES:
++            do_res (idx, line, 0);
++            return 1;
++          case K_SRES:
++            do_res (idx, line, 's');
++            return 1;
++          case K_INCLUDE:
++            do_include (idx, line);
++            return 1;
++          case K_LOCAL:
++            do_local (idx, line);
++            return 1;
++          case K_MACRO:
++            do_macro (idx, line);
++            return 1;
++          case K_ENDM:
++            do_endm ();
++            return 1;
++          case K_SRESC:
++            do_res (idx, line, 'c');
++            return 1;
++          case K_PRINT:
++            do_print (idx, line);
++            return 1;
++          case K_FORM:
++            do_form (idx, line);
++            return 1;
++          case K_HEADING:
++            do_heading (idx, line);
++            return 1;
++          case K_PAGE:
++            do_page ();
++            return 1;
++          case K_GLOBAL:
++          case K_EXPORT:
++            do_export (line);
++            return 1;
++          case K_IMPORT:
++            return 1;
++          case K_SRESZ:
++            do_res (idx, line, 'z');
++            return 1;
++          case K_IGNORED:
++            return 1;
++          case K_END:
++            do_end (line);
++            return 1;
++          case K_ASSIGNA:
++            do_assigna (idx, line);
++            return 1;
++          case K_ASSIGNC:
++            do_assignc (idx, line);
++            return 1;
++          case K_EXITM:
++            do_exitm ();
++            return 1;
++          case K_REG:
++            do_reg (idx, line);
++            return 1;
++          case K_IFEQ:
++            do_if (idx, line, EQ);
++            return 1;
++          case K_IFNE:
++            do_if (idx, line, NE);
++            return 1;
++          case K_IFLT:
++            do_if (idx, line, LT);
++            return 1;
++          case K_IFLE:
++            do_if (idx, line, LE);
++            return 1;
++          case K_IFGE:
++            do_if (idx, line, GE);
++            return 1;
++          case K_IFGT:
++            do_if (idx, line, GT);
++            return 1;
++          case K_IFC:
++            do_ifc (idx, line, 0);
++            return 1;
++          case K_IFNC:
++            do_ifc (idx, line, 1);
++            return 1;
++          case K_IRP:
++            do_irp (idx, line, 0);
++            return 1;
++          case K_IRPC:
++            do_irp (idx, line, 1);
++            return 1;
++          }
++      }
++    }
++  return 0;
++}
++
++/* Add a keyword to the hash table.  */
++
++static void
++add_keyword (name, code)
++     const char *name;
++     int code;
++{
++  sb label;
++  int j;
++
++  sb_new (&label);
++  sb_add_string (&label, name);
++
++  hash_add_to_int_table (&keyword_hash_table, &label, code);
++
++  sb_reset (&label);
++  for (j = 0; name[j]; j++)
++    sb_add_char (&label, name[j] - 'A' + 'a');
++  hash_add_to_int_table (&keyword_hash_table, &label, code);
++
++  sb_kill (&label);
++}
++
++/* Build the keyword hash table - put each keyword in the table twice,
++   once upper and once lower case.  */
++
++static void
++process_init ()
++{
++  int i;
++
++  for (i = 0; kinfo[i].name; i++)
++    add_keyword (kinfo[i].name, kinfo[i].code);
++
++  if (mri)
++    {
++      for (i = 0; mrikinfo[i].name; i++)
++      add_keyword (mrikinfo[i].name, mrikinfo[i].code);
++    }
++}
++
++static void
++do_define (string)
++     const char *string;
++{
++  sb label;
++  int res = 1;
++  hash_entry *ptr;
++  sb_new (&label);
++
++  while (*string)
++    {
++      if (*string == '=')
++      {
++        sb value;
++        sb_new (&value);
++        string++;
++        while (*string)
++          {
++            sb_add_char (&value, *string);
++            string++;
++          }
++        exp_get_abs (_("Invalid expression on command line.\n"),
++                     0, &value, &res);
++        sb_kill (&value);
++        break;
++      }
++      sb_add_char (&label, *string);
++
++      string++;
++    }
++
++  ptr = hash_create (&vars, &label);
++  free_old_entry (ptr);
++  ptr->type = hash_integer;
++  ptr->value.i = res;
++  sb_kill (&label);
++}
++
++char *program_name;
++
++/* The list of long options.  */
++static struct option long_options[] =
++{
++  { "alternate", no_argument, 0, 'a' },
++  { "include", required_argument, 0, 'I' },
++  { "commentchar", required_argument, 0, 'c' },
++  { "copysource", no_argument, 0, 's' },
++  { "debug", no_argument, 0, 'd' },
++  { "help", no_argument, 0, 'h' },
++  { "mri", no_argument, 0, 'M' },
++  { "output", required_argument, 0, 'o' },
++  { "print", no_argument, 0, 'p' },
++  { "unreasonable", no_argument, 0, 'u' },
++  { "version", no_argument, 0, 'v' },
++  { "define", required_argument, 0, 'd' },
++  { NULL, no_argument, 0, 0 }
++};
++
++/* Show a usage message and exit.  */
++static void
++show_usage (file, status)
++     FILE *file;
++     int status;
++{
++  fprintf (file, _("\
++Usage: %s \n\
++  [-a]      [--alternate]         enter alternate macro mode\n\
++  [-c char] [--commentchar char]  change the comment character from !\n\
++  [-d]      [--debug]             print some debugging info\n\
++  [-h]      [--help]              print this message\n\
++  [-M]      [--mri]               enter MRI compatibility mode\n\
++  [-o out]  [--output out]        set the output file\n\
++  [-p]      [--print]             print line numbers\n"), program_name);
++  fprintf (file, _("\
++  [-s]      [--copysource]        copy source through as comments \n\
++  [-u]      [--unreasonable]      allow unreasonable nesting\n\
++  [-v]      [--version]           print the program version\n\
++  [-Dname=value]                  create preprocessor variable called name, with value\n\
++  [-Ipath]                        add to include path list\n\
++  [in-file]\n"));
++  if (status == 0)
++    printf (_("Report bugs to %s\n"), REPORT_BUGS_TO);
++  exit (status);
++}
++
++/* Display a help message and exit.  */
++
++static void
++show_help ()
++{
++  printf (_("%s: Gnu Assembler Macro Preprocessor\n"), program_name);
++  show_usage (stdout, 0);
++}
++
++int main PARAMS ((int, char **));
++
++int
++main (argc, argv)
++     int argc;
++     char **argv;
++{
++  int opt;
++  char *out_name = 0;
++  sp = include_stack;
++
++  ifstack[0].on = 1;
++  ifi = 0;
++
++#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
++  setlocale (LC_MESSAGES, "");
++#endif
++#if defined (HAVE_SETLOCALE)
++  setlocale (LC_CTYPE, "");
++#endif
++  bindtextdomain (PACKAGE, LOCALEDIR);
++  textdomain (PACKAGE);
++
++  program_name = argv[0];
++  xmalloc_set_program_name (program_name);
++
++  hash_new_table (101, &keyword_hash_table);
++  hash_new_table (101, &assign_hash_table);
++  hash_new_table (101, &vars);
++
++  sb_new (&label);
++
++  while ((opt = getopt_long (argc, argv, "I:sdhavc:upo:D:M", long_options,
++                           (int *) NULL))
++       != EOF)
++    {
++      switch (opt)
++      {
++      case 'o':
++        out_name = optarg;
++        break;
++      case 'u':
++        unreasonable = 1;
++        break;
++      case 'I':
++        {
++          include_path *p = (include_path *) xmalloc (sizeof (include_path));
++          p->next = NULL;
++          sb_new (&p->path);
++          sb_add_string (&p->path, optarg);
++          if (paths_tail)
++            paths_tail->next = p;
++          else
++            paths_head = p;
++          paths_tail = p;
++        }
++        break;
++      case 'p':
++        print_line_number = 1;
++        break;
++      case 'c':
++        comment_char = optarg[0];
++        break;
++      case 'a':
++        alternate = 1;
++        break;
++      case 's':
++        copysource = 1;
++        break;
++      case 'd':
++        stats = 1;
++        break;
++      case 'D':
++        do_define (optarg);
++        break;
++      case 'M':
++        mri = 1;
++        comment_char = ';';
++        break;
++      case 'h':
++        show_help ();
++        /* NOTREACHED  */
++      case 'v':
++        /* This output is intended to follow the GNU standards document.  */
++        printf (_("GNU assembler pre-processor %s\n"), program_version);
++        printf (_("Copyright 1996 Free Software Foundation, Inc.\n"));
++        printf (_("\
++This program is free software; you may redistribute it under the terms of\n\
++the GNU General Public License.  This program has absolutely no warranty.\n"));
++        exit (0);
++        /* NOTREACHED  */
++      case 0:
++        break;
++      default:
++        show_usage (stderr, 1);
++        /* NOTREACHED  */
++      }
++    }
++
++  process_init ();
++
++  macro_init (alternate, mri, 0, exp_get_abs);
++
++  if (out_name)
++    {
++      outfile = fopen (out_name, "w");
++      if (!outfile)
++      {
++        fprintf (stderr, _("%s: Can't open output file `%s'.\n"),
++                 program_name, out_name);
++        exit (1);
++      }
++    }
++  else
++    {
++      outfile = stdout;
++    }
++
++  chartype_init ();
++  if (!outfile)
++    outfile = stdout;
++
++  /* Process all the input files.  */
++
++  while (optind < argc)
++    {
++      if (new_file (argv[optind]))
++      {
++        process_file ();
++      }
++      else
++      {
++        fprintf (stderr, _("%s: Can't open input file `%s'.\n"),
++                 program_name, argv[optind]);
++        exit (1);
++      }
++      optind++;
++    }
++
++  quit ();
++  return 0;
++}
++
++/* This function is used because an abort in some of the other files
++   may be compiled into as_abort because they include as.h.  */
++
++void
++as_abort (file, line, fn)
++     const char *file, *fn;
++     int line;
++{
++  fprintf (stderr, _("Internal error, aborting at %s line %d"), file, line);
++  if (fn)
++    fprintf (stderr, " in %s", fn);
++  fprintf (stderr, _("\nPlease report this bug.\n"));
++  exit (1);
++}
++
++/* duplicated from as internals */
++
++static void
++identify (char *file)
++{
++  static int identified;
++
++  if (identified)
++    return;
++  identified++;
++
++  if (file)
++    fprintf (stderr, "%s: ", file);
++  fprintf (stderr, _("Assembler messages:\n"));
++}
++
++/* The number of warnings issued.  */
++static int warning_count;
++
++/* The common portion of as_warn and as_warn_where.  */
++
++static void
++as_warn_internal (char *file, unsigned int line, char *buffer)
++{
++  ++warning_count;
++
++  identify (file);
++  if (file)
++    fprintf (stderr, "%s:%u: ", file, line);
++  fprintf (stderr, _("Warning: "));
++  fputs (buffer, stderr);
++  (void) putc ('\n', stderr);
++}
++
++/* Send to stderr a string 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.  */
++
++void
++as_warn (const char *format, ...)
++{
++  va_list args;
++  char buffer[2000];
++
++  if (1 /*!flag_no_warnings*/)
++    {
++      va_start (args, format);
++      vsprintf (buffer, format, args);
++      va_end (args);
++      as_warn_internal ((char *) NULL, 0, buffer);
++    }
++}
++
++/* Like as_bad but the file name and line number are passed in.
++   Unfortunately, we have to repeat the function in order to handle
++   the varargs correctly and portably.  */
++
++void
++as_warn_where (char *file, unsigned int line, const char *format, ...)
++{
++  va_list args;
++  char buffer[2000];
++
++  if (1 /*!flag_no_warnings*/)
++    {
++      va_start (args, format);
++      vsprintf (buffer, format, args);
++      va_end (args);
++      as_warn_internal (file, line, buffer);
++    }
++}
++
++/* 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;
++
++/* The common portion of as_bad and as_bad_where.  */
++
++static void
++as_bad_internal (char *file, unsigned int line, char *buffer)
++{
++  ++error_count;
++
++  identify (file);
++  if (file)
++    fprintf (stderr, "%s:%u: ", file, line);
++  fprintf (stderr, _("Error: "));
++  fputs (buffer, stderr);
++  (void) putc ('\n', stderr);
++}
++
++/* Send to stderr a string 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.  */
++
++void
++as_bad (const char *format, ...)
++{
++  va_list args;
++  char buffer[2000];
++
++  va_start (args, format);
++  vsprintf (buffer, format, args);
++  va_end (args);
++
++  as_bad_internal ((char *) NULL, 0, buffer);
++}
++
++/* Like as_bad but the file name and line number are passed in.
++   Unfortunately, we have to repeat the function in order to handle
++   the varargs correctly and portably.  */
++
++void
++as_bad_where (char *file, unsigned int line, const char *format, ...)
++{
++  va_list args;
++  char buffer[2000];
++
++  va_start (args, format);
++  vsprintf (buffer, format, args);
++  va_end (args);
++
++  as_bad_internal (file, line, buffer);
++}
++
++/* Indicate assertion failure.
++   Arguments: Filename, line number, optional function name.  */
++
++void
++as_assert (const char *file, int line, const char *fn)
++{
++  fprintf (stderr, _("Internal error!\n"));
++  if (fn)
++    fprintf (stderr, _("Assertion failure in %s at %s line %d.\n"),
++           fn, file, line);
++  else
++    fprintf (stderr, _("Assertion failure at %s line %d.\n"), file, line);
++  fprintf (stderr, _("Please report this bug.\n"));
++  xexit (EXIT_FAILURE);
++}
++
++#ifndef LEX_AT
++/* The m88k unfortunately uses @ as a label beginner.  */
++#define LEX_AT 0
++#endif
++
++#ifndef LEX_BR
++/* The RS/6000 assembler uses {,},[,] as parts of symbol names.  */
++#define LEX_BR 0
++#endif
++
++#ifndef LEX_PCT
++/* The Delta 68k assembler permits % inside label names.  */
++#define LEX_PCT 0
++#endif
++
++#ifndef LEX_QM
++/* The PowerPC Windows NT assemblers permits ? inside label names.  */
++#define LEX_QM 0
++#endif
++
++#ifndef LEX_HASH
++/* The IA-64 assembler uses # as a suffix designating a symbol.  We include
++   it in the symbol and strip it out in tc_canonicalize_symbol_name.  */
++#define LEX_HASH 0
++#endif
++
++#ifndef LEX_DOLLAR
++/* The a29k assembler does not permits labels to start with $.  */
++#define LEX_DOLLAR 3
++#endif
++
++#ifndef LEX_TILDE
++/* The Delta 68k assembler permits ~ at start of label names.  */
++#define LEX_TILDE 0
++#endif
++
++/* Used by is_... macros. our ctype[].  */
++char 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, LEX_HASH, LEX_DOLLAR, LEX_PCT, 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, LEX_QM,        /* 0123456789:;<=>? */
++  LEX_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, LEX_BR, 0, LEX_BR, 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, LEX_BR, 0, LEX_BR, LEX_TILDE, 0, /* pqrstuvwxyz{|}~.  */
++  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, 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, 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, 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, 3, 3, 3, 3, 3, 3, 3, 3,
++  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
++};
++
++/* just a stub to satisfy unused function in sb.o */
++int do_scrub_chars (int (*get) (char*, int), char *tostart, int tolen) {}
+--- binutils-2.16.91.0.2.org/gas/macro.c       2005-05-10 22:46:44.000000000 +0000
++++ binutils-2.16.91.0.2/gas/macro.c   2005-07-21 18:31:04.000000000 +0000
+@@ -41,8 +41,8 @@
+ static int get_apost_token (int, sb *, sb *, int);
+ static int sub_actual (int, sb *, sb *, struct hash_control *, int, sb *, int);
+ static const char *macro_expand_body
+-  (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *);
+-static const char *macro_expand (int, sb *, macro_entry *, sb *);
++  (sb *, sb *, formal_entry *, struct hash_control *, const macro_entry *, int);
++static const char *macro_expand (int, sb *, macro_entry *, sb *, int);
+ static void free_macro(macro_entry *);
+ #define ISWHITE(x) ((x) == ' ' || (x) == '\t')
+@@ -754,7 +754,7 @@
+ static const char *
+ macro_expand_body (sb *in, sb *out, formal_entry *formals,
+-                 struct hash_control *formal_hash, const macro_entry *macro)
++                 struct hash_control *formal_hash, const macro_entry *macro, int comment_char)
+ {
+   sb t;
+   int src = 0, inquote = 0, macro_line = 0;
+@@ -786,7 +786,14 @@
+       else if (in->ptr[src] == '\\')
+       {
+         src++;
+-        if (src < in->len && in->ptr[src] == '(')
++        if (in->ptr[src] == comment_char && comment_char != '\0')
++          {
++            /* This is a comment, just drop the rest of the line.  */
++            while (src < in->len
++                   && in->ptr[src] != '\n')
++              src++;
++          }
++        else if (src < in->len && in->ptr[src] == '(')
+           {
+             /* Sub in till the next ')' literally.  */
+             src++;
+@@ -867,7 +874,7 @@
+         else
+           {
+             src = sb_skip_white (src + 5, in);
+-            while (in->ptr[src] != '\n')
++            while (in->ptr[src] != '\n' && in->ptr[src] != comment_char)
+               {
+                 const char *name;
+                 formal_entry *f = new_formal ();
+@@ -903,6 +910,17 @@
+               }
+           }
+       }
++      else if (comment_char != '\0'
++             && in->ptr[src] == comment_char
++             && src + 1 < in->len
++             && in->ptr[src + 1] == comment_char
++             && !inquote)
++      {
++        /* Two comment chars in a row cause the rest of the line to
++           be dropped.  */
++        while (src < in->len && in->ptr[src] != '\n')
++          src++;
++      }
+       else if (in->ptr[src] == '"'
+              || (macro_mri && in->ptr[src] == '\''))
+       {
+@@ -984,7 +1002,7 @@
+    body.  */
+ static const char *
+-macro_expand (int idx, sb *in, macro_entry *m, sb *out)
++macro_expand (int idx, sb *in, macro_entry *m, sb *out, int comment_char)
+ {
+   sb t;
+   formal_entry *ptr;
+@@ -1029,7 +1047,7 @@
+   /* Peel off the actuals and store them away in the hash tables' actuals.  */
+   idx = sb_skip_white (idx, in);
+-  while (idx < in->len)
++  while (idx < in->len && in->ptr[idx] != comment_char)
+     {
+       int scan;
+@@ -1156,7 +1174,7 @@
+         sb_add_string (&ptr->actual, buffer);
+       }
+-      err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m);
++      err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m, comment_char);
+     }
+   /* Discard any unnamed formal arguments.  */
+@@ -1189,7 +1207,7 @@
+    *EXPAND.  Return 1 if a macro is found, 0 otherwise.  */
+ int
+-check_macro (const char *line, sb *expand,
++check_macro (const char *line, sb *expand, int comment_char,
+            const char **error, macro_entry **info)
+ {
+   const char *s;
+@@ -1224,7 +1242,7 @@
+     sb_add_char (&line_sb, *s++);
+   sb_new (expand);
+-  *error = macro_expand (0, &line_sb, macro, expand);
++  *error = macro_expand (0, &line_sb, macro, expand, comment_char);
+   sb_kill (&line_sb);
+@@ -1285,7 +1303,7 @@
+    success, or an error message otherwise.  */
+ const char *
+-expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *))
++expand_irp (int irpc, int idx, sb *in, sb *out, int (*get_line) (sb *), int comment_char)
+ {
+   sb sub;
+   formal_entry f;
+@@ -1318,10 +1336,10 @@
+   sb_reset (out);
+   idx = sb_skip_comma (idx, in);
+-  if (idx >= in->len)
++  if (idx >= in->len || in->ptr[idx] == comment_char)
+     {
+       /* Expand once with a null string.  */
+-      err = macro_expand_body (&sub, out, &f, h, 0);
++      err = macro_expand_body (&sub, out, &f, h, 0, comment_char);
+     }
+   else
+     {
+@@ -1333,7 +1351,7 @@
+         ++idx;
+       }
+-      while (idx < in->len)
++      while (idx < in->len && in->ptr[idx] != comment_char)
+       {
+         if (!irpc)
+           idx = get_any_string (idx, in, &f.actual);
+@@ -1347,7 +1365,7 @@
+                   in_quotes = ! in_quotes;
+         
+                 nxt = sb_skip_white (idx + 1, in);
+-                if (nxt >= in->len)
++                if (nxt >= in->len || in->ptr[nxt] == comment_char)
+                   {
+                     idx = nxt;
+                     break;
+@@ -1358,7 +1376,7 @@
+             ++idx;
+           }
+-        err = macro_expand_body (&sub, out, &f, h, 0);
++        err = macro_expand_body (&sub, out, &f, h, 0, comment_char);
+         if (err != NULL)
+           break;
+         if (!irpc)
+--- binutils-2.16.91.0.2.org/gas/macro.h       2005-05-10 22:46:44.000000000 +0000
++++ binutils-2.16.91.0.2/gas/macro.h   2005-07-21 18:31:04.000000000 +0000
+@@ -90,8 +90,8 @@
+ extern void macro_mri_mode (int);
+ extern const char *define_macro
+   (int, sb *, sb *, int (*) (sb *), char *, unsigned int, const char **);
+-extern int check_macro (const char *, sb *, const char **, macro_entry **);
++extern int check_macro (const char *, sb *, int, const char **, macro_entry **);
+ extern void delete_macro (const char *);
+-extern const char *expand_irp (int, int, sb *, sb *, int (*) (sb *));
++extern const char *expand_irp (int, int, sb *, sb *, int (*) (sb *), int);
+ #endif
+--- binutils-2.16.91.0.2.org/gas/read.c        2005-06-22 20:53:34.000000000 +0000
++++ binutils-2.16.91.0.2/gas/read.c    2005-07-21 18:31:04.000000000 +0000
+@@ -555,7 +555,7 @@
+   const char *err;
+   macro_entry *macro;
+-  if (check_macro (line, &out, &err, &macro))
++  if (check_macro (line, &out, '\0', &err, &macro))
+     {
+       if (err != NULL)
+       as_bad ("%s", err);
+@@ -1958,7 +1958,7 @@
+   sb_new (&out);
+-  err = expand_irp (irpc, 0, &s, &out, get_non_macro_line_sb);
++  err = expand_irp (irpc, 0, &s, &out, get_non_macro_line_sb, '\0');
+   if (err != NULL)
+     as_bad_where (file, line, "%s", err);
diff --git a/binutils-info.patch b/binutils-info.patch
new file mode 100644 (file)
index 0000000..90d85d8
--- /dev/null
@@ -0,0 +1,175 @@
+diff -urN binutils-2.12.90.0.1.org/bfd/doc/bfd.texinfo binutils-2.12.90.0.1/bfd/doc/bfd.texinfo
+--- binutils-2.12.90.0.1.org/bfd/doc/bfd.texinfo       Mon Mar 11 22:37:36 2002
++++ binutils-2.12.90.0.1/bfd/doc/bfd.texinfo   Mon Mar 11 22:37:52 2002
+@@ -21,9 +21,10 @@
+ @ifinfo
+ @format
+-START-INFO-DIR-ENTRY
+-* Bfd: (bfd).                   The Binary File Descriptor library.
+-END-INFO-DIR-ENTRY
++@dircategory Programming tools:
++@direntry
++* Bfd: (bfd).                         The Binary File Descriptor library
++@end direntry
+ @end format
+ @end ifinfo
+diff -urN binutils-2.12.90.0.1.org/etc/configure.texi binutils-2.12.90.0.1/etc/configure.texi
+--- binutils-2.12.90.0.1.org/etc/configure.texi        Mon Mar 11 22:37:37 2002
++++ binutils-2.12.90.0.1/etc/configure.texi    Mon Mar 11 22:37:52 2002
+@@ -5,9 +5,9 @@
+ @setchapternewpage off
+ @c %**end of header
+-@dircategory GNU admin
++@dircategory Programming tools:
+ @direntry
+-* configure: (configure).     The GNU configure and build system
++* configure: (configure).             The GNU configure and build system
+ @end direntry
+ @ifinfo
+--- binutils-2.18.50.0.1/etc/standards.texi.orig       2007-09-08 19:33:36.000000000 +0200
++++ binutils-2.18.50.0.1/etc/standards.texi    2007-09-15 21:32:14.252217649 +0200
+@@ -6,9 +6,9 @@
+ @set lastupdate July 22, 2007
+ @c %**end of header
+-@dircategory GNU organization
++@dircategory Miscellaneous:
+ @direntry
+-* Standards: (standards).         GNU coding standards.
++* Standards: (standards).             GNU coding standards
+ @end direntry
+ @c @setchapternewpage odd
+diff -urN binutils-2.12.90.0.1.org/gas/doc/as.texinfo binutils-2.12.90.0.1/gas/doc/as.texinfo
+--- binutils-2.12.90.0.1.org/gas/doc/as.texinfo        Mon Mar 11 22:37:38 2002
++++ binutils-2.12.90.0.1/gas/doc/as.texinfo    Mon Mar 11 22:40:25 2002
+@@ -117,10 +117,11 @@
+ @ifinfo
+ @format
+-START-INFO-DIR-ENTRY
+-* As: (as).                     The GNU assembler.
+-* Gas: (as).                    The GNU assembler.
+-END-INFO-DIR-ENTRY
++@dircategory Programming tools:
++@direntry
++* As: (as).                           The GNU assembler
++* Gas: (as).                          The GNU assembler
++@end direntry
+ @end format
+ @end ifinfo
+diff -urN binutils-2.14.90.0.7.org/gas/doc/gasp.texinfo binutils-2.14.90.0.7/gas/doc/gasp.texinfo
+--- binutils-2.14.90.0.7.org/gas/doc/gasp.texinfo      Mon Mar 11 22:37:38 2002
++++ binutils-2.14.90.0.7/gas/doc/gasp.texinfo  Mon Mar 11 22:37:52 2002
+@@ -14,9 +14,10 @@
+ @ifinfo
+ @format
+-START-INFO-DIR-ENTRY
+-* gasp: (gasp).                     The GNU Assembler Preprocessor
+-END-INFO-DIR-ENTRY
++@dircategory Programming tools:
++@direntry
++* gasp: (gasp).                               The GNU Assembler Preprocessor
++@end direntry
+ @end format
+ @end ifinfo
+diff -urN binutils-2.12.90.0.1.org/gprof/gprof.texi binutils-2.12.90.0.1/gprof/gprof.texi
+--- binutils-2.12.90.0.1.org/gprof/gprof.texi  Mon Mar 11 22:37:40 2002
++++ binutils-2.12.90.0.1/gprof/gprof.texi      Mon Mar 11 22:37:52 2002
+@@ -9,9 +9,10 @@
+ @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
+-* gprof: (gprof).                Profiling your program's execution
+-END-INFO-DIR-ENTRY
++@dircategory Programming tools:
++@direntry
++* gprof: (gprof).                     Profiling your program's execution
++@end direntry
+ @end format
+ @end ifinfo
+diff -urN binutils-2.12.90.0.1.org/ld/ld.texinfo binutils-2.12.90.0.1/ld/ld.texinfo
+--- binutils-2.12.90.0.1.org/ld/ld.texinfo     Mon Mar 11 22:37:41 2002
++++ binutils-2.12.90.0.1/ld/ld.texinfo Mon Mar 11 22:37:52 2002
+@@ -48,9 +48,10 @@
+ @ifinfo
+ @format
+-START-INFO-DIR-ENTRY
+-* Ld: (ld).                       The GNU linker.
+-END-INFO-DIR-ENTRY
++@dircategory Programming tools:
++@direntry
++* Ld: (ld).                           The GNU linker
++@end direntry
+ @end format
+ @end ifinfo
+@@ -4590,7 +4591,7 @@
+ @node BFD outline
+ @section How it works: an outline of BFD
+ @cindex opening object files
+-@include bfdsumm.texi
++@include ../bfd/doc/bfdsumm.texi
+ @end ifclear
+ @node Reporting Bugs
+--- binutils-2.18.50.0.5/binutils/doc/binutils.texi.orig       2008-03-14 19:23:02.000000000 +0100
++++ binutils-2.18.50.0.5/binutils/doc/binutils.texi    2008-03-18 08:27:03.340177183 +0100
+@@ -23,29 +23,25 @@
+ @c man end
+ @end copying
+-@dircategory Software development
++@dircategory Programming tools:
+ @direntry
+-* Binutils: (binutils).         The GNU binary utilities.
+-@end direntry
+-
+-@dircategory Individual utilities
+-@direntry
+-* addr2line: (binutils)addr2line. Convert addresses to file and line.
+-* ar: (binutils)ar.               Create, modify, and extract from archives.
+-* c++filt: (binutils)c++filt.   Filter to demangle encoded C++ symbols.
+-* cxxfilt: (binutils)c++filt.     MS-DOS name for c++filt.
+-* dlltool: (binutils)dlltool.   Create files needed to build and use DLLs.
+-* nlmconv: (binutils)nlmconv.     Converts object code into an NLM.
+-* nm: (binutils)nm.               List symbols from object files.
+-* objcopy: (binutils)objcopy.   Copy and translate object files.
+-* objdump: (binutils)objdump.     Display information from object files.
+-* ranlib: (binutils)ranlib.       Generate index to archive contents.
+-* readelf: (binutils)readelf.   Display the contents of ELF format files.
+-* size: (binutils)size.           List section sizes and total size.
+-* strings: (binutils)strings.     List printable strings from files.
+-* strip: (binutils)strip.         Discard symbols.
+-* windmc: (binutils)windmc.     Generator for Windows message resources.
+-* windres: (binutils)windres.   Manipulate Windows resources.
++* Binutils: (binutils).                       The GNU binary utilities
++* addr2line: (binutils)addr2line.     Convert addresses to file and line
++* ar: (binutils)ar.                   Create, modify, and extract from archives
++* c++filt: (binutils)c++filt.         Filter to demangle encoded C++ symbols
++* cxxfilt: (binutils)c++filt.         MS-DOS name for c++filt
++* dlltool: (binutils)dlltool.         Create files needed to build and use DLLs
++* nlmconv: (binutils)nlmconv.         Converts object code into an NLM
++* nm: (binutils)nm.                   List symbols from object files
++* objcopy: (binutils)objcopy.         Copy and translate object files
++* objdump: (binutils)objdump.         Display information from object files
++* ranlib: (binutils)ranlib.           Generate index to archive contents
++* readelf: (binutils)readelf.         Display the contents of ELF format files
++* size: (binutils)size.                       List section sizes and total size
++* strings: (binutils)strings.         List printable strings from files
++* strip: (binutils)strip.             Discard symbols
++* windmc: (binutils)windmc.           Generator for Windows message resources
++* windres: (binutils)windres.         Manipulate Windows resources
+ @end direntry
+ @titlepage
diff --git a/binutils-libtool-m.patch b/binutils-libtool-m.patch
new file mode 100644 (file)
index 0000000..cb7c518
--- /dev/null
@@ -0,0 +1,16 @@
+--- binutils-2.17.50.0.8/ltmain.sh.orig        2006-12-30 17:05:01.000000000 +0000
++++ binutils-2.17.50.0.8/ltmain.sh     2006-12-30 17:20:17.000000000 +0000
+@@ -1246,6 +1246,13 @@
+       continue
+       ;;
++      -m*|-pg)
++        compile_command="$compile_command $arg"
++      finalize_command="$finalize_command $arg"
++      compiler_flags="$compiler_flags $arg"
++      continue
++      ;;
++
+       -no-fast-install)
+       fast_install=no
+       continue
diff --git a/binutils-libtool-relink.patch b/binutils-libtool-relink.patch
new file mode 100644 (file)
index 0000000..d49678f
--- /dev/null
@@ -0,0 +1,33 @@
+--- binutils-2.17.50.0.17/ltmain.sh.orig       2007-06-18 19:29:28.000000000 +0200
++++ binutils-2.17.50.0.17/ltmain.sh    2007-06-21 22:27:40.096178020 +0200
+@@ -4306,6 +4306,7 @@
+         if test "$linkmode" = prog || test "$mode" = relink; then
+           add_shlibpath=
++          add_prefix_dir=
+           add_dir=
+           add=
+           # Finalize command for both is simple: just hardcode it.
+@@ -4342,10 +4343,22 @@
+             add="-l$name"
+           fi
++          if test -n "$inst_prefix_dir"; then
++            case "$libdir" in
++            [\\/]*)
++              add_prefix_dir="-L$inst_prefix_dir$libdir"
++              ;;
++            esac
++          fi
++
++          # add_prefix_dir must be appended instead, otherwise it can
++          # possibly be overrided by any hardcoded -L/... path in deplibs
+           if test "$linkmode" = prog; then
++            test -n "$add_prefix_dir" && finalize_deplibs="$finalize_deplibs $add_prefix_dir"
+             test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+             test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+           else
++            test -n "$add_prefix_dir" && deplibs="$deplibs $add_prefix_dir"
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+           fi
diff --git a/binutils-mips-relocs.patch b/binutils-mips-relocs.patch
new file mode 100644 (file)
index 0000000..e5e3d0a
--- /dev/null
@@ -0,0 +1,211 @@
+From binutils-return-33437-listarch-binutils=sources dot redhat dot com at sources dot redhat dot com Wed May 05 17:45:08 2004
+Return-Path: <binutils-return-33437-listarch-binutils=sources dot redhat dot com at sources dot redhat dot com>
+Delivered-To: listarch-binutils at sources dot redhat dot com
+Received: (qmail 20913 invoked by alias); 5 May 2004 17:45:08 -0000
+Mailing-List: contact binutils-help at sources dot redhat dot com; run by ezmlm
+Precedence: bulk
+List-Subscribe: <mailto:binutils-subscribe at sources dot redhat dot com>
+List-Archive: <http://sources.redhat.com/ml/binutils/>
+List-Post: <mailto:binutils at sources dot redhat dot com>
+List-Help: <mailto:binutils-help at sources dot redhat dot com>, <http://sources dot redhat dot com/ml/#faqs>
+Sender: binutils-owner at sources dot redhat dot com
+Delivered-To: mailing list binutils at sources dot redhat dot com
+Received: (qmail 20904 invoked from network); 5 May 2004 17:45:05 -0000
+Received: from unknown (HELO dmz.algor.co.uk) (62.254.210.145)
+  by sources dot redhat dot com with SMTP; 5 May 2004 17:45:05 -0000
+Received: from alg158.algor.co.uk ([62.254.210.158] helo=olympia.mips.com)
+       by dmz dot algor dot co dot uk with esmtp (Exim 3 dot 35 #1 (Debian))
+       id 1BLQgU-0007Tz-00; Wed, 05 May 2004 18:59:54 +0100
+Received: from stockwell.mips.com ([192.168.192.238])
+       by olympia dot mips dot com with esmtp (Exim 3 dot 36 #1 (Debian))
+       id 1BLQRk-0004Mi-00; Wed, 05 May 2004 18:44:40 +0100
+Subject: Re: [mips patch RFC] removal of gas embedded-pic support code
+From: David Ung <davidu at mips dot com>
+To: cgd at broadcom dot com, ica2_ts at csv dot ica dot uni-stuttgart dot de,  binutils at sources dot redhat dot com
+Content-Type: text/plain
+Organization: MIPS Technologies UK
+Message-Id: <1083779079.31797.223.camel@stockwell.mips.com>
+Mime-Version: 1.0
+Date: Wed, 05 May 2004 18:44:40 +0100
+Content-Transfer-Encoding: 7bit
+X-MTUK-Scanner: Found to be clean
+X-MTUK-SpamCheck: not spam, SpamAssassin (score=-4.833, required 4, AWL,
+       BAYES_00)
+
+>At Thu, 29 Apr 2004 02:46:46 +0000 (UTC), "Thiemo Seufer" wrote:
+>> I think it is ok.
+>
+>Thanks for looking it over.
+>
+>I've checked it in.  I *really* hope it really doesn't break
+>anything.  8-)
+>
+>
+>chris
+
+Chris,
+
+woops.  your recent patch seem to have broke building of the compiler on
+linux.  The problem occurs when building for libgcc/./_divdi3.o
+
+gas fails with:
+Error: operation combines symbols in different segments
+
+as it is trying to assemble this piece of .s code generated from gcc.
+
+        .section        .eh_frame,"aw",@progbits
+$Lframe1:
+        .4byte  $LECIE1-$LSCIE1
+$LSCIE1:
+        .4byte  0x0
+        .byte   0x1
+        .ascii  "zR\000"
+        .uleb128 0x1
+        .sleb128 4
+        .byte   0x1f
+        .uleb128 0x1
+        .byte   0x1b
+        .byte   0xc
+        .uleb128 0x1d
+        .uleb128 0x0
+        .align  2
+$LECIE1:
+$LSFDE1:
+        .4byte  $LEFDE1-$LASFDE1
+$LASFDE1:
+        .4byte  $LASFDE1-$Lframe1
+        .4byte  $LFB42-.               **
+        .4byte  $LFE42-$LFB42
+        .uleb128 0x0
+
+** $LFB42 is from the text section of divdi3.
+
+I've reverted back the necessary changes to your patch, so that it now
+works with the above.
+
+David.
+
+
+
+Index: gas/ChangeLog
+===================================================================
+RCS file: /cvsroot/gcc/src-cvs/src/gas/ChangeLog,v
+retrieving revision 1.2114
+diff -u -r1.2114 ChangeLog
+--- gas/ChangeLog      29 Apr 2004 05:14:20 -0000      1.2114
++++ gas/ChangeLog      5 May 2004 17:38:51 -0000
+@@ -1,0 +1,6 @@
++2004-05-05  David Ung  <davidu@mips.com>
++
++      * config/tc-mips.c (md_apply_fix3): Revert last change.
++      (tc_gen_reloc): Put back pc rel handling.
++      * config/tc-mips.h: Defines DIFF_EXPR_OK.
++
+Index: gas/config/tc-mips.c
+===================================================================
+RCS file: /cvsroot/gcc/src-cvs/src/gas/config/tc-mips.c,v
+retrieving revision 1.263
+diff -u -r1.263 tc-mips.c
+--- gas/config/tc-mips.c.orig  2004-11-22 21:33:32.000000000 +0100
++++ gas/config/tc-mips.c       2004-11-23 23:59:24.000000000 +0100
+@@ -10932,8 +10932,6 @@
+   buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where);
+-  assert (! fixP->fx_pcrel);
+-
+   /* Don't treat parts of a composite relocation as done.  There are two
+      reasons for this:
+@@ -10944,7 +10942,7 @@
+        constants.  The easiest way of dealing with the pathological
+        exceptions is to generate a relocation against STN_UNDEF and
+        leave everything up to the linker.  */
+-  if (fixP->fx_addsy == NULL && fixP->fx_tcbit == 0)
++  if (fixP->fx_addsy == NULL && (! fixP->fx_pcrel) && fixP->fx_tcbit == 0)
+     fixP->fx_done = 1;
+   switch (fixP->fx_r_type)
+@@ -12800,8 +12798,27 @@
+   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+-  assert (! fixp->fx_pcrel);
+-  reloc->addend = fixp->fx_addnumber;
++  if (fixp->fx_pcrel)
++    {
++      bfd_vma pcrel_address;
++
++      pcrel_address = reloc->address;
++
++      if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
++      {
++        /* At this point, fx_addnumber is "symbol offset - pcrel_address".
++           Relocations want only the symbol offset.  */
++        reloc->addend = fixp->fx_addnumber + pcrel_address;
++      }
++      else
++      {
++        /* A gruesome hack which is a result of the gruesome gas reloc
++           handling.  */
++        reloc->addend = pcrel_address;
++      }
++    }
++  else
++    reloc->addend = fixp->fx_addnumber;
+   /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable
+      entry to be used in the relocation's section offset.  */
+@@ -12812,6 +12829,34 @@
+     }
+   code = fixp->fx_r_type;
++  if (fixp->fx_pcrel)
++    {
++      switch (code)
++      {
++      case BFD_RELOC_8:
++        code = BFD_RELOC_8_PCREL;
++        break;
++      case BFD_RELOC_16:
++        code = BFD_RELOC_16_PCREL;
++        break;
++      case BFD_RELOC_32:
++        code = BFD_RELOC_32_PCREL;
++        break;
++      case BFD_RELOC_64:
++        code = BFD_RELOC_64_PCREL;
++        break;
++      case BFD_RELOC_8_PCREL:
++      case BFD_RELOC_16_PCREL:
++      case BFD_RELOC_32_PCREL:
++      case BFD_RELOC_64_PCREL:
++      case BFD_RELOC_16_PCREL_S2:
++        break;
++      default:
++        as_bad_where (fixp->fx_file, fixp->fx_line,
++                      _("Cannot make %s relocation PC relative"),
++                      bfd_get_reloc_code_name (code));
++      }
++    }
+   /* To support a PC relative reloc, we used a Cygnus extension.
+      We check for that here to make sure that we don't let such a
+Index: gas/config/tc-mips.h
+===================================================================
+RCS file: /cvsroot/gcc/src-cvs/src/gas/config/tc-mips.h,v
+retrieving revision 1.35
+diff -u -r1.35 tc-mips.h
+--- gas/config/tc-mips.h       29 Apr 2004 05:14:22 -0000      1.35
++++ gas/config/tc-mips.h       5 May 2004 17:38:51 -0000
+@@ -58,6 +58,10 @@
+ #define MAX_MEM_FOR_RS_ALIGN_CODE  (1 + 2)
++/* We permit PC relative difference expressions when generating
++   embedded PIC code.  */
++#define DIFF_EXPR_OK
++
+ /* Tell assembler that we have an itbl_mips.h header file to include.  */
+ #define HAVE_ITBL_CPU
diff --git a/binutils-pt_pax_flags.patch b/binutils-pt_pax_flags.patch
new file mode 100644 (file)
index 0000000..c9942a1
--- /dev/null
@@ -0,0 +1,236 @@
+--- binutils-2.15.94.0.2.2.orig/bfd/elf-bfd.h  2005-02-07 20:42:44.000000000 +0100
++++ binutils-2.15.94.0.2.2/bfd/elf-bfd.h       2005-02-20 13:13:17.362558200 +0100
+@@ -1266,6 +1266,9 @@
+   /* Should the PT_GNU_RELRO segment be emitted?  */
+   bfd_boolean relro;
++  /* Segment flags for the PT_PAX_FLAGS segment.  */
++  unsigned int pax_flags;
++
+   /* Symbol version definitions in external objects.  */
+   Elf_Internal_Verdef *verdef;
+--- binutils-2.17.50.0.18/bfd/elf.c.orig       2007-08-01 11:12:02.000000000 -0400
++++ binutils-2.17.50.0.18/bfd/elf.c    2007-08-01 14:27:36.086986774 -0400
+@@ -1085,6 +1085,7 @@
+     case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
+     case PT_GNU_STACK: pt = "STACK"; break;
+     case PT_GNU_RELRO: pt = "RELRO"; break;
++    case PT_PAX_FLAGS: pt = "PAX_FLAGS"; break;
+     default: pt = NULL; break;
+     }
+   return pt;
+@@ -2346,6 +2347,9 @@
+     case PT_GNU_RELRO:
+       return _bfd_elf_make_section_from_phdr (abfd, hdr, index, "relro");
++    case PT_PAX_FLAGS:
++      return _bfd_elf_make_section_from_phdr (abfd, hdr, index, "pax_flags");
++
+     default:
+       /* Check for any processor-specific program segment types.  */
+       bed = get_elf_backend_data (abfd);
+@@ -3326,6 +3330,11 @@
+       ++segs;
+     }
++    {
++      /* We need a PT_PAX_FLAGS segment.  */
++      ++segs;
++    }
++
+   for (s = abfd->sections; s != NULL; s = s->next)
+     {
+       if ((s->flags & SEC_LOAD) != 0
+@@ -3945,6 +3954,20 @@
+         pm = &m->next;
+       }
++      {
++        amt = sizeof (struct elf_segment_map);
++        m = bfd_zalloc (abfd, amt);
++        if (m == NULL)
++      goto error_return;
++        m->next = NULL;
++        m->p_type = PT_PAX_FLAGS;
++        m->p_flags = elf_tdata (abfd)->pax_flags;
++        m->p_flags_valid = 1;
++  
++        *pm = m;
++        pm = &m->next;
++      }
++
+       free (sections);
+       elf_tdata (abfd)->segment_map = mfirst;
+     }
+@@ -5129,7 +5152,8 @@
+        5. PT_GNU_STACK segments do not include any sections.
+        6. PT_TLS segment includes only SHF_TLS sections.
+        7. SHF_TLS sections are only in PT_TLS or PT_LOAD segments.
+-       8. PT_DYNAMIC should not contain empty sections at the beginning
++       8. PT_PAX_FLAGS segments do not include any sections.
++       9. PT_DYNAMIC should not contain empty sections at the beginning
+         (with the possible exception of .dynamic).  */
+ #define IS_SECTION_IN_INPUT_SEGMENT(section, segment, bed)            \
+   ((((segment->p_paddr                                                        \
+@@ -5138,6 +5162,7 @@
+      && (section->flags & SEC_ALLOC) != 0)                            \
+     || IS_COREFILE_NOTE (segment, section))                           \
+    && segment->p_type != PT_GNU_STACK                                 \
++   && segment->p_type != PT_PAX_FLAGS                                 \
+    && (segment->p_type != PT_TLS                                      \
+        || (section->flags & SEC_THREAD_LOCAL))                                \
+    && (segment->p_type == PT_LOAD                                     \
+--- binutils-2.17.50.0.13/bfd/elflink.c.orig   2007-03-16 20:44:46.377789106 +0100
++++ binutils-2.17.50.0.13/bfd/elflink.c        2007-03-16 20:45:24.781816352 +0100
+@@ -5296,16 +5296,30 @@
+     return TRUE;
+   bed = get_elf_backend_data (output_bfd);
++  elf_tdata (output_bfd)->pax_flags = PF_NORANDEXEC;
++
++  if (info->execheap)
++    elf_tdata (output_bfd)->pax_flags |= PF_NOMPROTECT;
++  else if (info->noexecheap)
++    elf_tdata (output_bfd)->pax_flags |= PF_MPROTECT;
++
+   if (info->execstack)
+-    elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
++    {
++      elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
++      elf_tdata (output_bfd)->pax_flags |= PF_EMUTRAMP;
++    }
+   else if (info->noexecstack)
+-    elf_tdata (output_bfd)->stack_flags = PF_R | PF_W;
++    {
++      elf_tdata (output_bfd)->stack_flags = PF_R | PF_W;
++      elf_tdata (output_bfd)->pax_flags |= PF_NOEMUTRAMP;
++    }
+   else
+     {
+       bfd *inputobj;
+       asection *notesec = NULL;
+       int exec = 0;
++      elf_tdata (output_bfd)->pax_flags |= PF_NOEMUTRAMP;
+       for (inputobj = info->input_bfds;
+          inputobj;
+          inputobj = inputobj->link_next)
+@@ -5318,7 +5332,11 @@
+         if (s)
+           {
+             if (s->flags & SEC_CODE)
+-              exec = PF_X;
++              {
++                elf_tdata (output_bfd)->pax_flags &= ~PF_NOEMUTRAMP;
++                elf_tdata (output_bfd)->pax_flags |= PF_EMUTRAMP;
++                exec = PF_X;
++              }
+             notesec = s;
+           }
+         else if (bed->default_execstack)
+--- binutils-2.15.94.0.2.2.orig/binutils/readelf.c     2005-02-18 07:14:30.000000000 +0100
++++ binutils-2.15.94.0.2.2/binutils/readelf.c  2005-02-20 13:13:17.470541784 +0100
+@@ -2293,6 +2293,7 @@
+                       return "GNU_EH_FRAME";
+     case PT_GNU_STACK:        return "GNU_STACK";
+     case PT_GNU_RELRO:  return "GNU_RELRO";
++    case PT_PAX_FLAGS:        return "PAX_FLAGS";
+     default:
+       if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC))
+--- binutils-2.15.94.0.2.2.orig/include/bfdlink.h      2004-11-22 21:33:32.000000000 +0100
++++ binutils-2.15.94.0.2.2/include/bfdlink.h   2005-02-20 13:13:17.476540872 +0100
+@@ -313,6 +313,14 @@
+      flags.  */
+   unsigned int noexecstack: 1;
++  /* TRUE if PT_PAX_FLAGS segment should be created with PF_NOMPROTECT
++     flags.  */
++  unsigned int execheap: 1;
++
++  /* TRUE if PT_PAX_FLAGS segment should be created with PF_MPROTECT
++     flags.  */
++  unsigned int noexecheap: 1;
++
+   /* TRUE if PT_GNU_RELRO segment should be created.  */
+   unsigned int relro: 1;
+--- binutils-2.15.94.0.2.2.orig/include/elf/common.h   2004-11-22 21:33:32.000000000 +0100
++++ binutils-2.15.94.0.2.2/include/elf/common.h        2005-02-20 13:13:17.482539960 +0100
+@@ -423,6 +423,7 @@
+ #define PT_GNU_STACK  (PT_LOOS + 0x474e551) /* Stack flags */
+ #define PT_GNU_RELRO  (PT_LOOS + 0x474e552) /* Read-only after relocation */
+ #define PT_GNU_SHR    (PT_LOOS + 0x474e554) /* Sharable segment */
++#define PT_PAX_FLAGS   (PT_LOOS + 0x5041580) /* PaX flags */
+ /* Program segment permissions, in program header p_flags field.  */
+@@ -433,6 +434,19 @@
+ #define PF_MASKOS     0x0FF00000      /* New value, Oct 4, 1999 Draft */
+ #define PF_MASKPROC   0xF0000000      /* Processor-specific reserved bits */
++#define PF_PAGEEXEC        (1 << 4)    /* Enable  PAGEEXEC */
++#define PF_NOPAGEEXEC  (1 << 5)    /* Disable PAGEEXEC */
++#define PF_SEGMEXEC        (1 << 6)    /* Enable  SEGMEXEC */
++#define PF_NOSEGMEXEC  (1 << 7)    /* Disable SEGMEXEC */
++#define PF_MPROTECT        (1 << 8)    /* Enable  MPROTECT */
++#define PF_NOMPROTECT  (1 << 9)    /* Disable MPROTECT */
++#define PF_RANDEXEC        (1 << 10)   /* Enable  RANDEXEC */
++#define PF_NORANDEXEC  (1 << 11)   /* Disable RANDEXEC */
++#define PF_EMUTRAMP        (1 << 12)   /* Enable  EMUTRAMP */
++#define PF_NOEMUTRAMP  (1 << 13)   /* Disable EMUTRAMP */
++#define PF_RANDMMAP        (1 << 14)   /* Enable  RANDMMAP */
++#define PF_NORANDMMAP  (1 << 15)   /* Disable RANDMMAP */
++
+ /* Values for section header, sh_type field.  */
+ #define SHT_NULL      0               /* Section header table entry unused */
+--- binutils-2.18.50.0.1/ld/emultempl/elf32.em.orig    2007-09-08 19:34:12.000000000 +0200
++++ binutils-2.18.50.0.1/ld/emultempl/elf32.em 2007-09-15 21:41:35.688212063 +0200
+@@ -2139,6 +2139,16 @@
+         link_info.noexecstack = TRUE;
+         link_info.execstack = FALSE;
+       }
++      else if (strcmp (optarg, "execheap") == 0)
++      {
++        link_info.execheap = TRUE;
++        link_info.noexecheap = FALSE;
++      }
++      else if (strcmp (optarg, "noexecheap") == 0)
++      {
++        link_info.noexecheap = TRUE;
++        link_info.execheap = FALSE;
++      }
+ EOF
+   if test -n "$COMMONPAGESIZE"; then
+@@ -2220,6 +2230,8 @@
+   fprintf (file, _("\
+   -z defs                     Report unresolved symbols in object files.\n"));
+   fprintf (file, _("\
++  -z execheap                 Mark executable as requiring executable heap\n"));
++  fprintf (file, _("\
+   -z execstack                Mark executable as requiring executable stack\n"));
+   fprintf (file, _("\
+   -z initfirst                Mark DSO to be initialized first at runtime\n"));
+@@ -2244,6 +2256,8 @@
+   fprintf (file, _("\
+   -z nodump                   Mark DSO not available to dldump\n"));
+   fprintf (file, _("\
++  -z noexecheap               Mark executable as not requiring executable heap\n"));
++  fprintf (file, _("\
+   -z noexecstack              Mark executable as not requiring executable stack\n"));
+ EOF
+--- binutils-2.15.94.0.2.2.orig/ld/ldgram.y    2004-11-22 21:33:32.000000000 +0100
++++ binutils-2.15.94.0.2.2/ld/ldgram.y 2005-02-20 13:13:17.499537376 +0100
+@@ -1073,6 +1073,8 @@
+                           $$ = exp_intop (0x6474e550);
+                         else if (strcmp (s, "PT_GNU_STACK") == 0)
+                           $$ = exp_intop (0x6474e551);
++                        else if (strcmp (s, "PT_PAX_FLAGS") == 0)
++                          $$ = exp_intop (0x65041580);
+                         else
+                           {
+                             einfo (_("\
diff --git a/binutils-tooldir.patch b/binutils-tooldir.patch
new file mode 100644 (file)
index 0000000..bc5d760
--- /dev/null
@@ -0,0 +1,23 @@
+--- binutils-2.19.51.0.4/configure.ac~ 2009-04-22 01:13:43.033504635 +0200
++++ binutils-2.19.51.0.4/configure.ac  2009-04-22 01:14:26.780226141 +0200
+@@ -20,7 +20,6 @@
+ ### WARNING: this file contains embedded tabs.  Do not run untabify on this file.
+ m4_include(config/acx.m4)
+-m4_include(config/override.m4)
+ m4_include(config/proginstall.m4)
+ AC_INIT(move-if-change)
+@@ -2113,7 +2112,11 @@
+ # Some systems (e.g., one of the i386-aix systems the gas testers are
+ # using) don't handle "\$" correctly, so don't use it here.
+-tooldir='${exec_prefix}'/${target_noncanonical}
++if test x${with_tooldir} != x ; then
++      tooldir="${with_tooldir}"
++else
++      tooldir='${exec_prefix}'/${target_noncanonical}
++fi
+ build_tooldir=${tooldir}
+ # Create a .gdbinit file which runs the one in srcdir
diff --git a/binutils.spec b/binutils.spec
new file mode 100644 (file)
index 0000000..20a008f
--- /dev/null
@@ -0,0 +1,322 @@
+#
+# Conditional build:
+%bcond_with    allarchs        # enable all targets
+# define addtargets x,y,z      # build with additional targets x,y,z (e.g. x86_64-linux)
+%bcond_with    gold            # enable gold (gnu ld successor) on supported archs (x86/sparc)
+                               # http://sourceware.org/ml/binutils/2008-03/msg00162.html
+%bcond_without pax             # without PaX flags (for upstream bugreports)
+#
+%ifnarch %{ix86} %{x8664} sparc sparc64 ppc ppc64
+%undefine      with_gold
+%endif
+#
+Summary:       GNU Binary Utility Development Utilities
+Summary(de.UTF-8):     GNU Binary Utility Development Utilities
+Summary(es.UTF-8):     Utilitarios para desarrollo de binarios de la GNU
+Summary(fr.UTF-8):     Utilitaires de développement binaire de GNU
+Summary(pl.UTF-8):     Narzędzia GNU dla programistów
+Summary(pt_BR.UTF-8):  Utilitários para desenvolvimento de binários da GNU
+Summary(ru.UTF-8):     Набор инструментов GNU для построения исполняемых программ
+Summary(tr.UTF-8):     GNU geliştirme araçları
+Summary(uk.UTF-8):     Набір інструментів GNU для побудови виконуваних програм
+Name:          binutils
+Version:       2.20.51.0.2
+Release:       1
+Epoch:         3
+License:       GPL v3+
+Group:         Development/Tools
+Source0:       ftp://ftp.kernel.org/pub/linux/devel/binutils/%{name}-%{version}.tar.bz2
+# Source0-md5: b01b185a5eab43190fb83efaeb2ffef9
+Source1:       http://www.mif.pg.gda.pl/homepages/ankry/man-PLD/%{name}-non-english-man-pages.tar.bz2
+# Source1-md5: a717d9707ec77d82acb6ec9078c472d6
+Patch0:                %{name}-gasp.patch
+Patch1:                %{name}-info.patch
+Patch2:                %{name}-libtool-relink.patch
+Patch3:                %{name}-pt_pax_flags.patch
+Patch5:                %{name}-flex.patch
+Patch6:                %{name}-discarded.patch
+Patch7:                %{name}-absolute-gnu_debuglink-path.patch
+Patch8:                %{name}-libtool-m.patch
+Patch9:                %{name}-build-id.patch
+Patch10:       %{name}-tooldir.patch
+URL:           http://sources.redhat.com/binutils/
+BuildRequires: autoconf >= 2.64
+BuildRequires: automake >= 1:1.11
+BuildRequires: bison
+BuildRequires: flex
+BuildRequires: gettext-devel
+%if %{with gold}
+BuildRequires: libstdc++-devel >= 6:4.0-1
+%endif
+BuildRequires: perl-tools-pod
+%ifarch sparc sparc32
+BuildRequires: sparc32
+%endif
+BuildRequires: texinfo >= 4.2
+Conflicts:     gcc-c++ < 5:3.3
+Conflicts:     modutils < 2.4.17
+BuildRoot:     %{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+
+%description
+Binutils is a collection of binary utilities, including:
+- ar - create, modify and extract from archives,
+- nm - lists symbols from object files,
+- objcopy - copy and translate object files,
+- objdump - display information from object files,
+- ranlib - generate an index for the contents of an archive,
+- size - list the section sizes of an object or archive file,
+- strings - list printable strings from files,
+- strip - discard symbols,
+- addr2line - convert addresses to file and line,
+- nlmconv - convert object code into an NLM.
+
+%description -l es.UTF-8
+Binutils es una colección de utilitarios necesarios para compilar
+programas. Incluye assembler y linker, así como varios otros programas
+para trabajar con formatos que se puedan ejecutar.
+
+%description -l pl.UTF-8
+Pakiet binutils zawiera zestaw narzędzi umożliwiających kompilację
+programów. Znajdują się tutaj między innymi assembler, konsolidator
+(linker), a także inne narzędzia do manipulowania binarnymi plikami
+programów i bibliotek.
+
+%description -l pt_BR.UTF-8
+binutils é uma coletânea de utilitários necessários para compilar
+programas. Inclui assembler e linker, assim como vários outros
+programas para trabalhar com formatos executáveis.
+
+%description -l ru.UTF-8
+binutils - это набор инструментов, необходимых для компилляции
+программ. Включает ассемблер, компоновщик и набор других программ для
+работы с исполняемыми файлами разнообразных форматов.
+
+%description -l uk.UTF-8
+binutils - це набір інструментів, необхідних для компіляції програм.
+Містить асемблер, компоновщик та інші програми, необхідні для роботи з
+виконуваними файлами різних форматів.
+
+%package libs
+Summary:       GNU binutils shared libraries
+Summary(pl.UTF-8):     Biblioteki współdzielone GNU binutils
+Group:         Libraries
+Conflicts:     binutils < 3:2.17.50.0.8-3
+
+%description libs
+GNU binutils shared libraries (libbfd, libopcodes).
+
+%description libs -l pl.UTF-8
+Biblioteki współdzielone GNU binutils (libbfd, libopcodes).
+
+%package devel
+Summary:       Development files for GNU binutils libraries
+Summary(pl.UTF-8):     Pliki programistyczne bibliotek GNU binutils
+Group:         Development/Libraries
+Requires:      %{name}-libs = %{epoch}:%{version}-%{release}
+
+%description devel
+Development files for GNU binutils libraries (libbfd, libopcodes) and
+static libiberty library.
+
+%description devel -l pl.UTF-8
+Pliki programistyczne bibliotek GNU binutils (libbfd, libopcodes) oraz
+statyczna biblioteka libiberty.
+
+%package static
+Summary:       GNU binutils static libraries
+Summary(pl.UTF-8):     Biblioteki statyczne do GNU binutils
+Group:         Development/Libraries
+Requires:      %{name}-devel = %{epoch}:%{version}-%{release}
+
+%description static
+Static GNU binutils libraries (libbfd, libopcodes).
+
+%description static -l pl.UTF-8
+Biblioteki statyczne GNU binutils (libbfd, libopcodes).
+
+%package gasp
+Summary:       GASP - old preprocessor for assembly programs
+Summary(pl.UTF-8):     GASP - stary preprocesor dla programów w asemblerze
+Group:         Development/Tools
+Requires:      %{name} = %{epoch}:%{version}-%{release}
+
+%description gasp
+GASP - old preprocessor for assembly programs. It's officially
+obsoleted, but it's still needed to build some packages.
+
+%description gasp -l pl.UTF-8
+GASP - stary preprocesor dla programów w asemblerze. Jest oficjalnie
+uznany za przestarzały, ale jest nadal potrzebny do zbudowania
+niektórych pakietów.
+
+%prep
+%setup -q
+%patch0 -p1
+%patch1 -p1
+%patch2 -p1
+%{?with_pax:%patch3 -p1}
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+
+# file contains hacks for ac 2.59 only
+rm config/override.m4
+
+%build
+%{__aclocal}
+%{__autoconf}
+
+# non-standard regeneration (needed because of gasp patch)
+# AM_BINUTILS_WARNINGS in bfd/warning.m4, ZW_GNU_GETTEXT_SISTER_DIR in config/gettext-sister.m4
+for dir in gas bfd; do
+       cd $dir || exit 1
+       %{__aclocal} -I .. -I ../config -I ../bfd
+       %{__automake} Makefile
+       %{__automake} doc/Makefile
+       %{__autoconf}
+       cd ..
+done
+
+# More targets
+TARGETS=
+%ifarch ia64
+TARGETS=i686-linux
+%endif
+# uhm?
+%ifarch %{ix86}
+TARGETS=x86_64-linux
+%endif
+%ifarch sparc
+TARGETS=sparc64-linux
+%endif
+%{?addtargets:TARGETS="%{addtargets}"}
+
+cp -f /usr/share/automake/config.* .
+CFLAGS="%{rpmcflags}"; export CFLAGS
+CC="%{__cc}"; export CC
+%ifarch sparc
+sparc32 \
+%endif
+./configure %{_target_platform} \
+       --disable-debug \
+       --disable-werror \
+       --enable-build-warnings=,-Wno-missing-prototypes \
+       --enable-shared \
+       --prefix=%{_prefix} \
+       --libdir=%{_libdir} \
+       --infodir=%{_infodir} \
+       --mandir=%{_mandir} \
+       --with-tooldir=%{_prefix} \
+       %{!?with_allarchs:`[ -n "${TARGETS}" ] && echo "--enable-targets=${TARGETS}"`} \
+%ifarch sparc
+       --enable-64-bit-bfd \
+%else
+       %{?with_allarchs:--enable-64-bit-bfd} \
+%endif
+       %{?with_allarchs:--enable-targets=alpha-linux,arm-linux,cris-linux,hppa-linux,i386-linux,ia64-linux,x86_64-linux,m68k-linux,mips-linux,mips64-linux,mips64el-linux,mipsel-linux,ppc-linux,s390-linux,s390x-linux,sh-linux,sparc-linux,sparc64-linux,i386-linuxaout} \
+       %{?with_gold:--enable-gold}
+
+%{__make}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+%{__make} install \
+       DESTDIR=$RPM_BUILD_ROOT
+
+rm $RPM_BUILD_ROOT%{_infodir}/standards.info*
+
+# remove these man pages unless we cross-build for win*/netware platforms.
+# however, this should be done in Makefiles.
+rm $RPM_BUILD_ROOT%{_mandir}/man1/{dlltool,nlmconv,windres}.1
+
+bzip2 -dc %{SOURCE1} | tar xf - -C $RPM_BUILD_ROOT%{_mandir}
+
+install include/libiberty.h $RPM_BUILD_ROOT%{_includedir}
+install libiberty/pic/libiberty.a $RPM_BUILD_ROOT%{_libdir}
+
+# remove evil -L pointing inside builder's home
+perl -pi -e 's@-L[^ ]*/pic @@g' $RPM_BUILD_ROOT%{_libdir}/libbfd.la
+
+rm -f $RPM_BUILD_ROOT%{_infodir}/dir
+
+%find_lang bfd
+%find_lang binutils
+%find_lang gas
+%find_lang gprof
+touch ld.lang
+%if %{without gold}
+%find_lang ld
+%endif
+%find_lang opcodes
+cat bfd.lang opcodes.lang > %{name}-libs.lang
+cat gas.lang gprof.lang ld.lang >> %{name}.lang
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post  -p /sbin/postshell
+-/usr/sbin/fix-info-dir -c %{_infodir}
+
+%postun        -p /sbin/postshell
+-/usr/sbin/fix-info-dir -c %{_infodir}
+
+%post  libs -p /sbin/ldconfig
+%postun        libs -p /sbin/ldconfig
+
+%post  devel -p /sbin/postshell
+-/usr/sbin/fix-info-dir -c %{_infodir}
+
+%postun        devel -p /sbin/postshell
+-/usr/sbin/fix-info-dir -c %{_infodir}
+
+%files -f %{name}.lang
+%defattr(644,root,root,755)
+%doc README
+%attr(755,root,root) %{_bindir}/[!g]*
+%attr(755,root,root) %{_bindir}/g[!a]*
+%{_infodir}/as.info*
+%{_infodir}/binutils.info*
+%{_infodir}/configure.info*
+%{_infodir}/gprof.info*
+%if %{without gold}
+%{_infodir}/ld.info*
+%{_prefix}/lib/ldscripts
+%endif
+%{_mandir}/man1/*
+%lang(cs) %{_mandir}/cs/man1/*
+%lang(de) %{_mandir}/de/man1/*
+%lang(es) %{_mandir}/es/man1/*
+%lang(fi) %{_mandir}/fi/man1/*
+%lang(fr) %{_mandir}/fr/man1/*
+%lang(hu) %{_mandir}/hu/man1/*
+%lang(ja) %{_mandir}/ja/man1/*
+%lang(pl) %{_mandir}/pl/man1/*
+
+%files libs -f %{name}-libs.lang
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/libbfd-*.so
+%attr(755,root,root) %{_libdir}/libopcodes-*.so
+
+%files devel
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_libdir}/libbfd.so
+%attr(755,root,root) %{_libdir}/libopcodes.so
+%{_libdir}/libbfd.la
+%{_libdir}/libopcodes.la
+%{_libdir}/libiberty.a
+%{_includedir}/*.h
+%{_infodir}/bfd.info*
+
+%files static
+%defattr(644,root,root,755)
+%{_libdir}/libbfd.a
+%{_libdir}/libopcodes.a
+
+%files gasp
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/gasp
+%{_infodir}/gasp.info*
This page took 0.530694 seconds and 4 git commands to generate.