]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add support for macros.
authorIan Lance Taylor <ian@airs.com>
Mon, 21 Aug 1995 18:35:11 +0000 (18:35 +0000)
committerIan Lance Taylor <ian@airs.com>
Mon, 21 Aug 1995 18:35:11 +0000 (18:35 +0000)
* as.c: Include sb.h and macro.h.
(max_macro_next): New global variable.
(main): Call macro_init.
(macro_expr): New static function.
* as.h (max_macro_nest): Declare.
* read.c (line_label): Rename from mri_line_label.  Change all
uses.
(potable): Add exitm, irp, irpc, macro, mexit, rept.
(read_a_source_file): Always clear line_label at the start of a
line, not just when flag_mri or LABELS_WITHOUT_COLONS.  Fixup
MRI/LABELS_WITHOUT_COLONS handling.  In MRI mode, permit label:
equ val.  Set line_label when calling colon.  In MRI mode, a
leading '.' does not imply a pseudo-op.  Check for macro expansion
before calling md_assemble.
(s_irp): New function.
(get_line_sb): New static function.
(s_macro): New function.
(s_mexit): New function.
(s_rept): New function.
* read.h (line_label): Rename from mri_line_label.
(s_irp, s_rept): Declare.
(s_macro, s_mexit): Declare.
* input-scrub.c: Include sb.h.
(sb_index, from_sb): New static variables.
(macro_nest): New static variable.
(struct input_save): Add sb_index and from_sb fields.  Change
next_saved_file field to be struct input_save *.
(next_saved_file): Changed to be struct input_save *.
(input_scrub_push): Change to return type struct input_save *.
Save sb_index and from_sb.
(input_scrub_pop): Change parameter type to struct input_save *.
Restore sb_index and from_sb.
(input_scrub_include_sb): New function.
(input_scrub_next_buffer): Handle reading from from_sb.
(bump_line_counters): Only increment lines if not using from_sb.
* config/tc-m68k.c (opt_table): Add nest.
(opt_nest): New static function.
* gasp.c: Include sb.h and macro.h.  Move all sb related functions
and definitions to sb.h and sb.c.  Move all macro related
functions and definitions to macro.h and macro.c.
* sb.h, sb.c: New files, extracted from gasp.c.
* macro.h, macro.c: Likewise.
* Makefile.in (OBJS): Add sb.o and macro.o
(GASPOBJS): Define.
(gasp.new): Depend upon $(GASPOBJS).  Use $(GASPOBJS) to link.
(TARG_CPU_DEP_m68k): Depend upon subsegs.h.
(gasp.o): Depend upon sb.h and macro.h.
(sb.o): New target.
(macro.o): New target.
(as.o): Depend upon sb.h and macro.h.
(input-scrub.o): Depend upon sb.h.
(read.o): Depend upon sb.h and macro.h.

12 files changed:
gas/.Sanitize
gas/ChangeLog
gas/Makefile.in
gas/NEWS
gas/as.c
gas/as.h
gas/config/tc-m68k.c
gas/gasp.c
gas/input-scrub.c
gas/macro.h [new file with mode: 0644]
gas/read.c
gas/sb.c [new file with mode: 0644]

index 75d45c5444335e95d1a59ce40cab9dab0327009b..c5bae552792af0dbb385b36190c19cb667c0b205 100644 (file)
@@ -76,6 +76,8 @@ link.cmd
 listing.c
 listing.h
 literal.c
+macro.c
+macro.h
 messages.c
 mpw-config.in
 mpw-make.in
@@ -84,6 +86,8 @@ output-file.c
 output-file.h
 read.c
 read.h
+sb.c
+sb.h
 stabs.c
 struc-symbol.h
 subsegs.c
index 786bb167d6a12499ae97214904e46e46ad8b7037..3a44e5c405d805044dfdbec24563254117abc35c 100644 (file)
@@ -1,3 +1,67 @@
+Mon Aug 21 13:57:20 1995  Ian Lance Taylor  <ian@cygnus.com>
+
+       Add support for macros.
+       * as.c: Include sb.h and macro.h.
+       (max_macro_next): New global variable.
+       (main): Call macro_init.
+       (macro_expr): New static function.
+       * as.h (max_macro_nest): Declare.
+       * read.c (line_label): Rename from mri_line_label.  Change all
+       uses.
+       (potable): Add exitm, irp, irpc, macro, mexit, rept.
+       (read_a_source_file): Always clear line_label at the start of a
+       line, not just when flag_mri or LABELS_WITHOUT_COLONS.  Fixup
+       MRI/LABELS_WITHOUT_COLONS handling.  In MRI mode, permit label:
+       equ val.  Set line_label when calling colon.  In MRI mode, a
+       leading '.' does not imply a pseudo-op.  Check for macro expansion
+       before calling md_assemble.
+       (s_irp): New function.
+       (get_line_sb): New static function.
+       (s_macro): New function.
+       (s_mexit): New function.
+       (s_rept): New function.
+       * read.h (line_label): Rename from mri_line_label.
+       (s_irp, s_rept): Declare.
+       (s_macro, s_mexit): Declare.
+       * input-scrub.c: Include sb.h.
+       (sb_index, from_sb): New static variables.
+       (macro_nest): New static variable.
+       (struct input_save): Add sb_index and from_sb fields.  Change
+       next_saved_file field to be struct input_save *.
+       (next_saved_file): Changed to be struct input_save *.
+       (input_scrub_push): Change to return type struct input_save *.
+       Save sb_index and from_sb.
+       (input_scrub_pop): Change parameter type to struct input_save *.
+       Restore sb_index and from_sb.
+       (input_scrub_include_sb): New function.
+       (input_scrub_next_buffer): Handle reading from from_sb.
+       (bump_line_counters): Only increment lines if not using from_sb.
+       * config/tc-m68k.c (opt_table): Add nest.
+       (opt_nest): New static function.
+       * gasp.c: Include sb.h and macro.h.  Move all sb related functions
+       and definitions to sb.h and sb.c.  Move all macro related
+       functions and definitions to macro.h and macro.c.
+       * sb.h, sb.c: New files, extracted from gasp.c.
+       * macro.h, macro.c: Likewise.
+       * Makefile.in (OBJS): Add sb.o and macro.o
+       (GASPOBJS): Define.
+       (gasp.new): Depend upon $(GASPOBJS).  Use $(GASPOBJS) to link.
+       (TARG_CPU_DEP_m68k): Depend upon subsegs.h.
+       (gasp.o): Depend upon sb.h and macro.h.
+       (sb.o): New target.
+       (macro.o): New target.
+       (as.o): Depend upon sb.h and macro.h.
+       (input-scrub.o): Depend upon sb.h.
+       (read.o): Depend upon sb.h and macro.h.
+
+       * cond.c (get_mri_string): New static function.
+       (s_ifc): New function.
+       * read.c (potable): Add ifc and ifnc.
+       * read.h (s_ifc): Declare.
+
+       * app.c (do_scrub_begin): In MRI mode, set lex of ' to
+       LEX_IS_STRINGQUOTE.
+
 Mon Aug 21 13:41:33 1995  Michael Meissner  <meissner@cygnus.com>
 
        * config/tc-ppc.c (md_assemble): Allow @HA, @L, and @H suffixes on
index dc1d11291830949f25b6ace11415e6e06785d71f..c997b59947a730aac33c9edba27716a8953bd091 100644 (file)
@@ -163,6 +163,8 @@ LINKED_HEADERS = \
 
 HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS)
 
+TE_OBJS=
+
 # @target_frag@
 
 OBJS = \
@@ -192,9 +194,17 @@ OBJS = \
        listing.o \
        ecoff.o \
        stabs.o \
+       sb.o \
+       macro.o \
        @extra_objects@ \
        $(TE_OBJS)
 
+GASPOBJS = \
+       gasp.o \
+       macro.o \
+       sb.o \
+       hash.o
+
 all: .gdbinit as.new gasp.new
        @srcroot=`cd $(srcroot); pwd`; export srcroot; \
        (cd doc ; $(MAKE) $(FLAGS_TO_PASS) all)
@@ -245,8 +255,8 @@ $(OBJS): config.h as.h targ-env.h obj-format.h targ-cpu.h flonum.h expr.h \
        struc-symbol.h write.h frags.h hash.h read.h symbols.h tc.h obj.h \
        listing.h bignum.h
 
-gasp.new: gasp.o
-       $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o gasp.new gasp.o $(LIBS) $(LOADLIBES)
+gasp.new: $(GASPOBJS)
+       $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o gasp.new $(GASPOBJS) $(LIBS) $(LOADLIBES)
 
 installcheck:
        @echo No installcheck target is available yet for the GNU assembler.
@@ -291,7 +301,7 @@ TARG_CPU_DEP_i386    = $(srcdir)/../include/opcode/i386.h
 TARG_CPU_DEP_i860    =
 TARG_CPU_DEP_i960    =
 TARG_CPU_DEP_m68k    = $(srcdir)/../include/opcode/m68k.h \
-                       $(srcdir)/config/m68k-parse.h
+                       $(srcdir)/config/m68k-parse.h subsegs.h
 TARG_CPU_DEP_m88k    = $(srcdir)/config/m88k-opcode.h
 TARG_CPU_DEP_mips    = $(srcdir)/../include/opcode/mips.h
 TARG_CPU_DEP_ns32k   =
@@ -306,9 +316,11 @@ TARG_CPU_DEP_vax     =
 TARG_CPU_DEP_w65     = $(srcdir)/../opcodes/w65-opc.h
 TARG_CPU_DEP_z8k     = $(srcdir)/../opcodes/z8k-opc.h
 
-gasp.o : gasp.c config.h
+gasp.o : gasp.c sb.h macro.h config.h
+sb.o : sb.c sb.h config.h
+macro.o : macro.c macro.h sb.h hash.h config.h
 app.o : app.c write.h
-as.o : as.c output-file.h write.h subsegs.h
+as.o : as.c output-file.h write.h subsegs.h sb.h macro.h
 atof-generic.o : atof-generic.c
 bignum-copy.o : bignum-copy.c
 cond.o : cond.c
@@ -320,12 +332,12 @@ flonum-mult.o : flonum-mult.c
 frags.o : frags.c subsegs.h
 hash.o : hash.c
 input-file.o : input-file.c input-file.h
-input-scrub.o : input-scrub.c input-file.h
+input-scrub.o : input-scrub.c input-file.h sb.h
 listing.o : listing.c input-file.h subsegs.h
 literal.o : literal.c subsegs.h
 messages.o : messages.c
 output-file.o : output-file.c output-file.h
-read.o : read.c
+read.o : read.c sb.h macro.h
 subsegs.o : subsegs.c subsegs.h
 symbols.o : symbols.c subsegs.h
 write.o : write.c subsegs.h output-file.h
index c35108e7d5ecd898c54fb81fcc33042949b62a60..ee4d9a1e8022e725142f88a4378e3403ce5c18e6 100644 (file)
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,15 @@
 -*- text -*-
 
+Changes since 2.5:
+
+Gas now directly supports macros, without requiring GASP.
+
+Gas now has an MRI assembler compatibility mode.
+
+Added -mips4 support to MIPS assembler.
+
+Added PIC support to Solaris and SPARC SunOS 4 assembler.
+
 Changes since 2.3:
 
 Converted this directory to use an autoconf-generated configure script.
@@ -18,17 +28,19 @@ used, it should become obvious pretty quickly what the problem is.
 
 Usage message is available with "--help".
 
+The GNU Assembler Preprocessor (gasp) is included.  (Actually, it was in 2.3
+also, but didn't get into the NEWS file.)
+
 Weak symbol support for a.out.
 
 A bug in the listing code which could cause an infinite loop has been fixed.
 Bugs in listings when generating a COFF object file have also been fixed.
 
 Initial i386-svr4 PIC implementation from Eric Youngdale, based on code by Paul
-Kranenburg.  This code was oriented towards gas version 1.xx; in updating some
-aspects of it for version 2, I broke it.
+Kranenburg.
 
 Improved Alpha support.  Immediate constants can have a much larger range now.
-Support for the 21164 has been added.
+Support for the 21164 has been contributed by Digital.
 
 Updated ns32k (pc532-mach, netbsd532) support from Ian Dall.
 
index e2da5cb963a2b710fb6dd324a3660129b23e108f..4850e866bb145fabbc06448bcd2b0de703141d7e 100644 (file)
--- a/gas/as.c
+++ b/gas/as.c
@@ -15,7 +15,7 @@
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
 
 /*
  * Main program for AS; a 32-bit assembler of GNU.
 #include "as.h"
 #include "subsegs.h"
 #include "output-file.h"
+#include "sb.h"
+#include "macro.h"
 
 static void perform_an_assembly_pass PARAMS ((int argc, char **argv));
+static int macro_expr PARAMS ((const char *, int, sb *, int *));
 
 int listing;                   /* true if a listing is wanted */
 
 static char *listing_filename = NULL;  /* Name of listing file.  */
 
+/* Maximum level of macro nesting.  */
+
+int max_macro_nest = 100;
+
 char *myname;                  /* argv[0] */
 #ifdef BFD_ASSEMBLER
 segT reg_section, expr_section;
@@ -94,6 +101,7 @@ Options:\n\
 -K                     warn when differences altered for long displacements\n\
 -L                     keep local symbols (starting with `L')\n");
   fprintf (stream, "\
+-M,--mri               assemble in MRI compatibility mode\n\
 -nocpp                 ignored\n\
 -o OBJFILE             name the object-file output OBJFILE (default a.out)\n\
 -R                     fold data section into text section\n\
@@ -224,7 +232,7 @@ parse_args (pargc, pargv)
       /* -K is not meaningful if .word is not being hacked.  */
       'K',
 #endif
-      'L', 'R', 'W', 'Z', 'f', 'a', ':', ':', 'D', 'I', ':', 'o', ':',
+      'L', 'M', 'R', 'W', 'Z', 'f', 'a', ':', ':', 'D', 'I', ':', 'o', ':',
 #ifndef VMS
       /* -v takes an argument on VMS, so we don't make it a generic
          option.  */
@@ -239,6 +247,7 @@ parse_args (pargc, pargv)
   static const struct option std_longopts[] = {
 #define OPTION_HELP (OPTION_STD_BASE)
     {"help", no_argument, NULL, OPTION_HELP},
+    {"mri", no_argument, NULL, 'M'},
 #define OPTION_NOCPP (OPTION_STD_BASE + 1)
     {"nocpp", no_argument, NULL, OPTION_NOCPP},
 #define OPTION_STATISTICS (OPTION_STD_BASE + 2)
@@ -370,6 +379,10 @@ parse_args (pargc, pargv)
          flag_keep_locals = 1;
          break;
 
+       case 'M':
+         flag_mri = 1;
+         break;
+
        case 'R':
          flag_readonly_data_in_text = 1;
          break;
@@ -499,9 +512,10 @@ main (argc, argv)
   frag_init ();
   subsegs_begin ();
   read_begin ();
-  input_scrub_begin ();
-  PROGRESS (1);
   parse_args (&argc, &argv);
+  input_scrub_begin ();
+  expr_begin ();
+  macro_init (0, flag_mri, macro_expr);
 
   PROGRESS (1);
 
@@ -683,4 +697,32 @@ perform_an_assembly_pass (argc, argv)
     read_a_source_file ("");
 }                              /* perform_an_assembly_pass() */
 
+/* The interface between the macro code and gas expression handling.  */
+
+static int
+macro_expr (emsg, idx, in, val)
+     const char *emsg;
+     int idx;
+     sb *in;
+     int *val;
+{
+  char *hold;
+  expressionS ex;
+
+  sb_terminate (in);
+
+  hold = input_line_pointer;
+  input_line_pointer = in->ptr + idx;
+  expression (&ex);
+  idx = input_line_pointer - in->ptr;
+  input_line_pointer = hold;
+
+  if (ex.X_op != O_constant)
+    as_bad ("%s", emsg);
+
+  *val = (int) ex.X_add_number;
+
+  return idx;
+}
+
 /* end of as.c */
index 3cd5c58e10e91ce0121e45448595dc189a21cc66..76d75d6d7a121c8ed411c2c987e012b830b1ffaa 100644 (file)
--- a/gas/as.h
+++ b/gas/as.h
@@ -519,6 +519,9 @@ COMMON int linkrelax;
 /* TRUE if we should produce a listing.  */
 extern int listing;
 
+/* Maximum level of macro nesting.  */
+extern int max_macro_nest;
+
 struct _pseudo_type
   {
     /* assembler mnemonic, lower case, no '.' */
index b212d507c27c924d9787ce81c3378fc336ce638e..fe3ee54b5e107f978782b6c86e12609f8d86789f 100644 (file)
@@ -4243,6 +4243,7 @@ struct opt_action
 /* The table used to handle the MRI OPT pseudo-op.  */
 
 static void skip_to_comma PARAMS ((int, int));
+static void opt_nest PARAMS ((int, int));
 static void opt_chip PARAMS ((int, int));
 static void opt_list PARAMS ((int, int));
 static void opt_list_symbols PARAMS ((int, int));
@@ -4274,6 +4275,7 @@ static const struct opt_action opt_table[] =
   { "mex", 0, 0, 0, 0 },
   { "mc", 0, 0, 0, 0 },
   { "md", 0, 0, 0, 0 },
+  { "nest", opt_nest, 0, 0, 0 },
   { "next", skip_to_comma, 0, 0, 0 },
   { "o", 0, 0, 0, 0 },
   { "old", 0, 0, 0, 0 },
@@ -4373,6 +4375,23 @@ skip_to_comma (arg, on)
     ++input_line_pointer;
 }
 
+/* Handle the OPT NEST=depth option.  */
+
+static void
+opt_nest (arg, on)
+     int arg;
+     int on;
+{
+  if (*input_line_pointer != '=')
+    {
+      as_bad ("bad format of OPT NEST=depth");
+      return;
+    }
+
+  ++input_line_pointer;
+  max_macro_nest = get_absolute_expression ();
+}
+
 /* Handle the OPT P=chip option.  */
 
 static void
@@ -4424,7 +4443,7 @@ s_reg (ignore)
   struct m68k_op rop;
   unsigned long mask;
 
-  if (mri_line_label == NULL)
+  if (line_label == NULL)
     {
       as_bad ("missing label");
       ignore_rest_of_line ();
@@ -4481,9 +4500,9 @@ s_reg (ignore)
       return;
     }
 
-  S_SET_SEGMENT (mri_line_label, absolute_section);
-  S_SET_VALUE (mri_line_label, mask);
-  mri_line_label->sy_frag = &zero_address_frag;
+  S_SET_SEGMENT (line_label, absolute_section);
+  S_SET_VALUE (line_label, mask);
+  line_label->sy_frag = &zero_address_frag;
 
   demand_empty_rest_of_line ();
 }
index 0380543b8846510cae89b5ad599bde3fc2db898d..1e242202a41d108da35e0881661d07718444c4c8 100644 (file)
@@ -65,6 +65,8 @@ extern char *malloc ();
 
 #include "ansidecl.h"
 #include "libiberty.h"
+#include "sb.h"
+#include "macro.h"
 
 char *program_version = "1.2";
 
@@ -88,58 +90,6 @@ int had_end; /* Seen .END */
 /* The output stream */
 FILE *outfile;
 
-/* string blocks
-
-   I had a couple of choices when deciding upon this data structure.
-   gas uses null terminated strings for all its internal work.  This
-   often means that parts of the program that want to examine
-   substrings have to manipulate the data in the string to do the
-   right thing (a common operation is to single out a bit of text by
-   saving away the character after it, nulling it out, operating on
-   the substring and then replacing the character which was under the
-   null).  This is a pain and I remember a load of problems that I had with
-   code in gas which almost got this right.  Also, it's harder to grow and
-   allocate null terminated strings efficiently.
-
-   Obstacks provide all the functionality needed, but are too
-   complicated, hence the sb.
-
-   An sb is allocated by the caller, and is initialzed to point to an
-   sb_element.  sb_elements are kept on a free lists, and used when
-   needed, replaced onto the free list when unused.
- */
-
-#define max_power_two    30    /* don't allow strings more than
-                                  2^max_power_two long */
-/* structure of an sb */
-typedef struct sb
-  {
-    char *ptr;                 /* points to the current block. */
-    int len;                   /* how much is used. */
-    int pot;                   /* the maximum length is 1<<pot */
-    struct le *item;
-  }
-sb;
-
-/* Structure of the free list object of an sb */
-typedef struct le
-  {
-    struct le *next;
-    int size;
-    char data[1];
-  }
-sb_element;
-
-/* The free list */
-typedef struct
-  {
-    sb_element *size[max_power_two];
-  } sb_list_vector;
-
-sb_list_vector free_list;
-
-int string_count[max_power_two];
-
 /* the attributes of each character are stored as a bit pattern
    chartype, which gives us quick tests. */
 
@@ -298,10 +248,6 @@ include_stack[MAX_INCLUDES];
 struct include_stack *sp;
 #define isp (sp - include_stack)
 
-#define dsize 5
-
-
-
 /* Include file list */
 
 typedef struct include_path 
@@ -315,20 +261,6 @@ include_path *paths_tail;
 
 
 static void quit PARAMS ((void));
-static void sb_build PARAMS ((sb *, int));
-static void sb_new PARAMS ((sb *));
-static void sb_kill PARAMS ((sb *));
-static void sb_add_sb PARAMS ((sb *, sb *));
-static void sb_check PARAMS ((sb *, int));
-static void sb_reset PARAMS ((sb *));
-static void sb_add_char PARAMS ((sb *, int));
-static void sb_add_string PARAMS ((sb *, const char *));
-static void sb_add_buffer PARAMS ((sb *, const char *, int));
-static void sb_print PARAMS ((sb *));
-static void sb_print_at PARAMS ((int, sb *));
-static char *sb_name PARAMS ((sb *));
-static int sb_skip_white PARAMS ((int, sb *));
-static int sb_skip_comma PARAMS ((int, sb *));
 static void hash_new_table PARAMS ((int, hash_table *));
 static int hash PARAMS ((sb *));
 static hash_entry *hash_create PARAMS ((hash_table *, sb *));
@@ -393,7 +325,6 @@ 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 buffer_and_nest PARAMS ((const char *, const char *, sb *));
 static void do_aendr PARAMS ((void));
 static void do_awhile PARAMS ((int, sb *));
 static void do_aendw PARAMS ((void));
@@ -401,16 +332,8 @@ 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 int do_formals PARAMS ((macro_entry *, int, sb *));
 static void do_local PARAMS ((int, sb *));
 static void do_macro PARAMS ((int, sb *));
-static int get_token PARAMS ((int, sb *, sb *));
-static int get_apost_token PARAMS ((int, sb *, sb *, int));
-static int sub_actual
-  PARAMS ((int, sb *, sb *, hash_table *, int, sb *, int));
-static void macro_expand_body
-  PARAMS ((sb *, sb *, sb *, formal_entry *, hash_table *));
-static void macro_expand PARAMS ((sb *, int, sb *, macro_entry *));
 static int macro_op PARAMS ((int, sb *));
 static int getstring PARAMS ((int, sb *, sb *));
 static void do_sdata PARAMS ((int, sb *, int));
@@ -451,7 +374,7 @@ quit ()
   if (stats) 
     {
       int i;
-      for (i = 0; i < max_power_two; i++) 
+      for (i = 0; i < sb_max_power_two; i++) 
        {
          fprintf (stderr, "strings size %8d : %d\n", 1<<i, string_count[i]);
        }
@@ -459,241 +382,6 @@ quit ()
   exit (exitcode);
 }
 
-
-/* this program is about manipulating strings.
-   they are managed in things called `sb's which is an abbreviation
-   for string buffers.  an sb has to be created, things can be glued
-   on to it, and at the end of it's life it should be freed.  the
-   contents should never be pointed at whilst it is still growing,
-   since it could be moved at any time
-
-   eg:
-   sb_new (&foo);
-   sb_grow... (&foo,...);
-   use foo->ptr[*];
-   sb_kill (&foo);
-
-*/
-
-/* initializes an sb. */
-
-static void
-sb_build (ptr, size)
-     sb *ptr;
-     int size;
-{
-  /* see if we can find one to allocate */
-  sb_element *e;
-
-  if (size > max_power_two)
-    {
-      FATAL ((stderr, "string longer than %d bytes requested.\n",
-             1 << max_power_two));
-    }
-  e = free_list.size[size];
-  if (!e)
-    {
-      /* nothing there, allocate one and stick into the free list */
-      e = (sb_element *) xmalloc (sizeof (sb_element) + (1 << size));
-      e->next = free_list.size[size];
-      e->size = 1 << size;
-      free_list.size[size] = e;
-      string_count[size]++;
-    }
-
-  /* remove from free list */
-
-  free_list.size[size] = e->next;
-
-  /* copy into callers world */
-  ptr->ptr = e->data;
-  ptr->pot = size;
-  ptr->len = 0;
-  ptr->item = e;
-}
-
-
-static void
-sb_new (ptr)
-     sb *ptr;
-{
-  sb_build (ptr, dsize);
-}
-
-/* deallocate the sb at ptr */
-
-static
-void
-sb_kill (ptr)
-     sb *ptr;
-{
-  /* return item to free list */
-  ptr->item->next = free_list.size[ptr->pot];
-  free_list.size[ptr->pot] = ptr->item;
-}
-
-/* add the sb at s to the end of the sb at ptr */
-
-static void sb_check ();
-
-static
-void
-sb_add_sb (ptr, s)
-     sb *ptr;
-     sb *s;
-{
-  sb_check (ptr, s->len);
-  memcpy (ptr->ptr + ptr->len, s->ptr, s->len);
-  ptr->len += s->len;
-}
-
-/* make sure that the sb at ptr has room for another len characters,
-   and grow it if it doesn't. */
-
-static void
-sb_check (ptr, len)
-     sb *ptr;
-     int len;
-{
-  if (ptr->len + len >= 1 << ptr->pot)
-    {
-      sb tmp;
-      int pot = ptr->pot;
-      while (ptr->len + len >= 1 << pot)
-       pot++;
-      sb_build (&tmp, pot);
-      sb_add_sb (&tmp, ptr);
-      sb_kill (ptr);
-      *ptr = tmp;
-    }
-}
-
-/* make the sb at ptr point back to the beginning.  */
-
-static void
-sb_reset (ptr)
-     sb *ptr;
-{
-  ptr->len = 0;
-}
-
-/* add character c to the end of the sb at ptr. */
-
-static void
-sb_add_char (ptr, c)
-     sb *ptr;
-     int c;
-{
-  sb_check (ptr, 1);
-  ptr->ptr[ptr->len++] = c;
-}
-
-/* add null terminated string s to the end of sb at ptr. */
-
-static void
-sb_add_string (ptr, s)
-     sb *ptr;
-     const char *s;
-{
-  int len = strlen (s);
-  sb_check (ptr, len);
-  memcpy (ptr->ptr + ptr->len, s, len);
-  ptr->len += len;
-}
-
-/* add string at s of length len to sb at ptr */
-
-static void
-sb_add_buffer (ptr, s, len)
-     sb *ptr;
-     const char *s;
-     int len;
-{
-  sb_check (ptr, len);
-  memcpy (ptr->ptr + ptr->len, s, len);
-  ptr->len += len;
-}
-
-
-/* print the sb at ptr to the output file */
-
-static
-void
-sb_print (ptr)
-     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;
-    }
-}
-
-static void 
-sb_print_at (idx, ptr)
-     int idx;
-     sb *ptr;
-{
-  int i;
-  for (i = idx; i < ptr->len; i++)
-    putc (ptr->ptr[i], outfile);
-}
-/* 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 (in)
-     sb *in;
-{
-  /* stick a null on the end of the string */
-  sb_add_char (in, 0);
-  return in->ptr;
-}
-
-/* start at the index idx into the string in sb at ptr and skip
-   whitespace. return the index of the first non whitespace character */
-
-static int
-sb_skip_white (idx, ptr)
-     int idx;
-     sb *ptr;
-{
-  while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
-    idx++;
-  return idx;
-}
-
-/* start at the index idx into the sb at ptr. skips whitespace,
-   a comma and any following whitespace. returnes the index of the
-   next character. */
-
-static int
-sb_skip_comma (idx, ptr)
-     int idx;
-     sb *ptr;
-{
-  while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
-    idx++;
-
-  if (idx < ptr->len
-      && ptr->ptr[idx] == ',')
-    idx++;
-
-  while (idx < ptr->len && ISWHITE (ptr->ptr[idx]))
-    idx++;
-
-  return idx;
-}
-
-
 /* hash table maintenance. */
 
 /* build a new hash table with size buckets, and fill in the info at ptr. */
@@ -1680,7 +1368,7 @@ do_data (idx, in, size)
        }
     }
   sb_kill (&acc);
-  sb_print_at (idx, in);
+  sb_print_at (outfile, idx, in);
   fprintf (outfile, "\n");
 }
 
@@ -2703,89 +2391,6 @@ do_ifc (idx, in, ifnc)
   ifstack[ifi].hadelse = 0;
 }
 
-/* Read input lines till we get to a TO string.
-   Increase nesting depth if we geta FROM string.
-   Put the results into sb at PTR. */
-
-static void
-buffer_and_nest (from, to, ptr)
-     const char *from;
-     const char *to;
-     sb *ptr;
-{
-  int from_len = strlen (from);
-  int to_len = strlen (to);
-  int depth = 1;
-  int line_start = ptr->len;
-  int line = linecount ();
-
-  int more = get_line (ptr);
-
-  while (more)
-    {
-      /* Try and find the first pseudo op on the line */
-      int i = line_start;
-
-      if (!alternate && !mri)
-       {
-         /* With normal syntax we can suck what we want till we get
-            to the dot.  With the alternate, labels have to start in
-            the first column, since we cant tell what's a label and
-            whats a pseudoop */
-
-         /* Skip leading whitespace */
-         while (i < ptr->len
-                && ISWHITE (ptr->ptr[i]))
-           i++;
-
-         /* Skip over a label */
-         while (i < ptr->len
-                && ISNEXTCHAR (ptr->ptr[i]))
-           i++;
-
-         /* And a colon */
-         if (i < ptr->len
-             && ptr->ptr[i] == ':')
-           i++;
-
-       }
-      /* Skip trailing whitespace */
-      while (i < ptr->len
-            && ISWHITE (ptr->ptr[i]))
-       i++;
-
-      if (i < ptr->len && (ptr->ptr[i] == '.' 
-                          || alternate
-                          || mri))
-       {
-         if (ptr->ptr[i] == '.')
-             i++;
-         if (strncasecmp (ptr->ptr + i, from, from_len) == 0)
-           depth++;
-         if (strncasecmp (ptr->ptr + i, to, to_len) == 0)
-           {
-             depth--;
-             if (depth == 0)
-               {
-                 /* Reset the string to not include the ending rune */
-                 ptr->len = line_start;
-                 break;
-               }
-           }
-       }
-
-      /* Add a CR to the end and keep running */
-      sb_add_char (ptr, '\n');
-      line_start = ptr->len;
-      more = get_line (ptr);
-    }
-
-
-  if (depth)
-    FATAL ((stderr, "End of file whilst inside %s, started on line %d.\n", from, line));
-}
-
-
 /* .ENDR */
 static void
 do_aendr ()
@@ -2804,18 +2409,19 @@ 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);
 
-  buffer_and_nest ("AWHILE", "AENDW", &sub);
+  if (! buffer_and_nest ("AWHILE", "AENDW", &sub, get_line))
+    FATAL ((stderr, "AWHILE without a AENDW at %d.\n", line - 1));
 
   /* Turn
        .AWHILE exp
@@ -2886,20 +2492,25 @@ 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)
-    buffer_and_nest ("AREPEAT", "AENDR", &sub);
+    ret = buffer_and_nest ("AREPEAT", "AENDR", &sub, get_line);
   else
-    buffer_and_nest ("REPT", "ENDR", &sub);
+    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
@@ -2952,174 +2563,22 @@ do_irp (idx, in, irpc)
      sb *in;
      int irpc;
 {
-  const char *mn;
-  sb sub;
-  formal_entry f;
-  hash_table h;
-  hash_entry *p;
-  sb name;
+  const char *err;
   sb out;
 
-  if (irpc)
-    mn = "IRPC";
-  else
-    mn = "IRP";
-
-  idx = sb_skip_white (idx, in);
-
-  sb_new (&sub);
-  buffer_and_nest (mn, "ENDR", &sub);
-  
-  sb_new (&f.name);
-  sb_new (&f.def);
-  sb_new (&f.actual);
-
-  idx = get_token (idx, in, &f.name);
-  if (f.name.len == 0)
-    {
-      ERROR ((stderr, "Missing model parameter in %s", mn));
-      return;
-    }
-
-  hash_new_table (1, &h);
-  p = hash_create (&h, &f.name);
-  p->type = hash_formal;
-  p->value.f = &f;
-
-  f.index = 1;
-  f.next = NULL;
-
-  sb_new (&name);
-  sb_add_string (&name, mn);
-
   sb_new (&out);
 
-  idx = sb_skip_comma (idx, in);
-  if (eol (idx, in))
-    {
-      /* Expand once with a null string.  */
-      macro_expand_body (&name, &sub, &out, &f, &h);
-      fprintf (outfile, "%s", sb_name (&out));
-    }
-  else
-    {
-      while (!eol (idx, in))
-       {
-         if (!irpc)
-           idx = get_any_string (idx, in, &f.actual, 1, 0);
-         else
-           {
-             sb_reset (&f.actual);
-             sb_add_char (&f.actual, in->ptr[idx]);
-             ++idx;
-           }
-         sb_reset (&out);
-         macro_expand_body (&name, &sub, &out, &f, &h);
-         fprintf (outfile, "%s", sb_name (&out));
-         if (!irpc)
-           idx = sb_skip_comma (idx, in);
-         else
-           idx = sb_skip_white (idx, in);
-       }
-    }
+  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 (&sub);
-  sb_kill (&name);
   sb_kill (&out);
 }
 
 /* MACRO PROCESSING */
 
-static int number;
-hash_table macro_table;
-
-/* Understand
-
-   .MACRO <name>
-   stuff
-   .ENDM
-*/
-
-static int
-do_formals (macro, idx, in)
-     macro_entry *macro;
-     int idx;
-     sb *in;
-{
-  formal_entry **p = &macro->formals;
-  macro->formal_count = 0;
-  hash_new_table (5, &macro->formal_hash);
-  while (idx < in->len)
-    {
-      formal_entry *formal;
-
-      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
-      sb_new (&formal->name);
-      sb_new (&formal->def);
-      sb_new (&formal->actual);
-
-      idx = sb_skip_white (idx, in);
-      idx = get_token (idx, in, &formal->name);
-      if (formal->name.len == 0)
-       break;
-      idx = sb_skip_white (idx, in);
-      if (formal->name.len)
-       {
-         /* This is a formal */
-         if (idx < in->len && in->ptr[idx] == '=')
-           {
-             /* Got a default */
-             idx = get_any_string (idx + 1, in, &formal->def, 1, 0);
-           }
-       }
-
-      {
-       /* Add to macro's hash table */
-
-       hash_entry *p = hash_create (&macro->formal_hash, &formal->name);
-       p->type = hash_formal;
-       p->value.f = formal;
-      }
-
-      formal->index = macro->formal_count;
-      idx = sb_skip_comma (idx, in);
-      macro->formal_count++;
-      *p = formal;
-      p = &formal->next;
-      *p = NULL;
-    }
-
-  if (mri)
-    {
-      formal_entry *formal;
-
-      /* Add a special NARG formal, which macro_expand will set to the
-         number of arguments.  */
-      formal = (formal_entry *) xmalloc (sizeof (formal_entry));
-
-      sb_new (&formal->name);
-      sb_new (&formal->def);
-      sb_new (&formal->actual);
-
-      sb_add_string (&formal->name, "NARG");
-
-      {
-       /* Add to macro's hash table */
-
-       hash_entry *p = hash_create (&macro->formal_hash, &formal->name);
-       p->type = hash_formal;
-       p->value.f = formal;
-      }
-
-      formal->index = -2;
-      *p = formal;
-      formal->next = NULL;
-    }
-
-  return idx;
-}
-
 /* Parse off LOCAL n1, n2,... Invent a label name for it */
 static
 void 
@@ -3127,519 +2586,52 @@ do_local (idx, line)
      int idx;
      sb *line;
 {
-  static int ln;
-  sb acc;
-  sb sub;
-  char subs[10];
-  sb_new (&acc);
-  sb_new (&sub);
-  idx = sb_skip_white (idx, line);
-  while (!eol(idx, line))
-    {
-      sb_reset (&acc);
-      sb_reset (&sub);
-      ln++;
-      sprintf(subs, "LL%04x", ln);
-      idx =  get_token(idx, line, &acc);
-      sb_add_string (&sub, subs);
-      hash_add_to_string_table (&assign_hash_table, &acc, &sub, 1);
-      idx = sb_skip_comma (idx, line);
-    }
-  sb_kill (&sub);
-  sb_kill (&acc);
+  ERROR ((stderr, "LOCAL outside of MACRO"));
 }
 
-static
-void
+static void
 do_macro (idx, in)
      int idx;
      sb *in;
 {
-  macro_entry *macro;
-  sb name;
-
-  macro = (macro_entry *) xmalloc (sizeof (macro_entry));
-  sb_new (&macro->sub);
-  sb_new (&name);
-
-  macro->formal_count = 0;
-  macro->formals = 0;
-
-  idx = sb_skip_white (idx, in);
-  buffer_and_nest ("MACRO", "ENDM", &macro->sub);
-  if (label.len)
-    {
-
-      sb_add_sb (&name, &label);
-      if (in->ptr[idx] == '(')
-       {
-         /* It's the label: MACRO (formals,...)  sort */
-         idx = do_formals (macro, idx + 1, in);
-         if (in->ptr[idx] != ')')
-           ERROR ((stderr, "Missing ) after formals.\n"));
-       }
-      else {
-       /* It's the label: MACRO formals,...  sort */
-       idx = do_formals (macro, idx, in);
-      }
-    }
-  else
-    {
-      idx = get_token (idx, in, &name);
-      idx = sb_skip_white (idx, in);
-      idx = do_formals (macro, idx, in);
-    }
-
-  /* and stick it in the macro hash table */
-  hash_create (&macro_table, &name)->value.m = macro;
-}
-
-static
-int
-get_token (idx, in, name)
-     int idx;
-     sb *in;
-     sb *name;
-{
-  if (idx < in->len
-      && ISFIRSTCHAR (in->ptr[idx]))
-    {
-      sb_add_char (name, in->ptr[idx++]);
-      while (idx < in->len
-            && ISNEXTCHAR (in->ptr[idx]))
-       {
-         sb_add_char (name, in->ptr[idx++]);
-       }
-    }
-  /* Ignore trailing & */
-  if (alternate && idx < in->len && in->ptr[idx] == '&')
-    idx++;
-  return idx;
-}
+  const char *err;
+  int line = linecount ();
 
-/* Scan a token, but stop if a ' is seen */
-static int
-get_apost_token (idx, in, name, kind)
-     int idx;
-     sb *in;
-     sb *name;
-     int kind;
-{
-  idx = get_token (idx, in, name);
-  if (idx < in->len && in->ptr[idx] == kind)
-    idx++;
-  return idx;
+  err = define_macro (idx, in, &label, get_line);
+  if (err != NULL)
+    ERROR ((stderr, "macro at line %d: %s\n", line - 1, err));
 }
 
 static int
-sub_actual (src, in, t, formal_hash, kind, out, copyifnotthere)
-     int src;
-     sb *in;
-     sb *t;
-     hash_table *formal_hash;
-     int kind;
-     sb *out;
-     int copyifnotthere;
-{
-  /* This is something to take care of */
-  hash_entry *ptr;
-  src = get_apost_token (src, in, t, kind);
-  /* See if it's in the macro's hash table */
-  ptr = hash_lookup (formal_hash, t);
-  if (ptr)
-    {
-      if (ptr->value.f->actual.len)
-       {
-         sb_add_sb (out, &ptr->value.f->actual);
-       }
-      else
-       {
-         sb_add_sb (out, &ptr->value.f->def);
-       }
-    }
-  else if (copyifnotthere)
-    {
-      sb_add_sb (out, t);
-    }
-  else 
-    {
-      sb_add_char (out, '\\');
-      sb_add_sb (out, t);
-    }
-  return src;
-}
-
-/* Copy the body from the macro buffer into a safe place and
-   substitute any args.  */
-
-static void
-macro_expand_body (name, in, out, formals, formal_hash)
-     sb *name;
-     sb *in;
-     sb *out;
-     formal_entry *formals;
-     hash_table *formal_hash;
-{
-  sb t;
-  int src = 0;
-  int inquote = 0;
-
-  sb_new (&t);
-
-  while (src < in->len)
-    {
-      if (in->ptr[src] == '&')
-       {
-         sb_reset (&t);
-         if (mri && src + 1 < in->len && in->ptr[src + 1] == '&')
-           {
-             src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1);
-           }
-         else
-           {
-             src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0);
-           }
-       }
-      else if (in->ptr[src] == '\\')
-       {
-         src++;
-         if (in->ptr[src] == comment_char)
-           {
-             /* This is a comment, just drop the rest of the line */
-             while (src < in->len
-                    && in->ptr[src] != '\n')
-               src++;
-
-           }
-         else if (in->ptr[src] == '(')
-           {
-             /* Sub in till the next ')' literally */
-             src++;
-             while (src < in->len && in->ptr[src] != ')')
-               {
-                 sb_add_char (out, in->ptr[src++]);
-               }
-             if (in->ptr[src] == ')')
-               src++;
-             else
-               ERROR ((stderr, "Missplaced ).\n"));
-           }
-         else if (in->ptr[src] == '@')
-           {
-             /* Sub in the macro invocation number */
-
-             char buffer[6];
-             src++;
-             sprintf (buffer, "%05d", number);
-             sb_add_string (out, buffer);
-           }
-         else if (in->ptr[src] == '&')
-           {
-             /* This is a preprocessor variable name, we don't do them
-                here */
-             sb_add_char (out, '\\');
-             sb_add_char (out, '&');
-             src++;
-           }
-         else if (mri
-                  && isalnum ((unsigned char) in->ptr[src]))
-           {
-             int ind;
-             formal_entry *f;
-
-             if (isdigit ((unsigned char) in->ptr[src]))
-               ind = in->ptr[src] - '0';
-             else if (isupper ((unsigned char) in->ptr[src]))
-               ind = in->ptr[src] - 'A' + 10;
-             else
-               ind = in->ptr[src] - 'a' + 10;
-             ++src;
-             for (f = formals; f != NULL; f = f->next)
-               {
-                 if (f->index == ind - 1)
-                   {
-                     if (f->actual.len != 0)
-                       sb_add_sb (out, &f->actual);
-                     else
-                       sb_add_sb (out, &f->def);
-                     break;
-                   }
-               }
-           }
-         else
-           {
-             sb_reset (&t);
-             src = sub_actual (src, in, &t, formal_hash, '\'', out, 0);
-           }
-       }
-      else if (ISFIRSTCHAR (in->ptr[src]) && (alternate || mri))
-       {
-         sb_reset (&t);
-         src = sub_actual (src, in, &t, formal_hash, '\'', out, 1);
-       }
-      else if (ISCOMMENTCHAR (in->ptr[src])
-              && src + 1 <  in->len
-              && ISCOMMENTCHAR (in->ptr[src+1])
-              && !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] == '"'
-              || (mri && in->ptr[src] == '\''))
-       {
-         inquote = !inquote;
-         sb_add_char (out, in->ptr[src++]);
-       }
-      else if (mri
-              && in->ptr[src] == '='
-              && src + 1 < in->len
-              && in->ptr[src + 1] == '=')
-       {
-         hash_entry *ptr;
-
-         sb_reset (&t);
-         src = get_token (src + 2, in, &t);
-         ptr = hash_lookup (formal_hash, &t);
-         if (ptr == NULL)
-           {
-             ERROR ((stderr, "MACRO formal argument %s does not exist.\n",
-                     sb_name (&t)));
-           }
-         else
-           {
-             if (ptr->value.f->actual.len)
-               {
-                 sb_add_string (out, "-1");
-               }
-             else
-               {
-                 sb_add_char (out, '0');
-               }
-           }
-       }
-      else
-       {
-         sb_add_char (out, in->ptr[src++]);
-       }
-    }
-
-  sb_kill (&t);
-}
-
-static void
-macro_expand (name, idx, in, m)
-     sb *name;
+macro_op (idx, in)
      int idx;
      sb *in;
-     macro_entry *m;
 {
-  sb t;
+  const char *err;
   sb out;
-  hash_entry *ptr;
-  formal_entry *f;
-  int is_positional = 0;
-  int is_keyword = 0;
-  int narg = 0;
-
-  sb_new (&t);
-  sb_new (&out);
-  
-  /* Reset any old value the actuals may have */
-  for (f = m->formals; f; f = f->next)
-      sb_reset (&f->actual);
-  f = m->formals;
-
-  if (mri)
-    {
-      /* The macro may be called with an optional qualifier, which may
-         be referred to in the macro body as \0.  */
-      if (idx < in->len && in->ptr[idx] == '.')
-       {
-         formal_entry *n;
-
-         n = (formal_entry *) xmalloc (sizeof (formal_entry));
-         sb_new (&n->name);
-         sb_new (&n->def);
-         sb_new (&n->actual);
-         n->index = -1;
-
-         n->next = m->formals;
-         m->formals = n;
-
-         idx = get_any_string (idx + 1, in, &n->actual, 1, 0);
-       }
-    }
-
-  /* Peel off the actuals and store them away in the hash tables' actuals */
-  while (!eol(idx, in))
-    {
-      int scan;
-      idx = sb_skip_white (idx, in);
-      /* Look and see if it's a positional or keyword arg */
-      scan = idx;
-      while (scan < in->len
-            && !ISSEP (in->ptr[scan])
-            && (!alternate && in->ptr[scan] != '='))
-       scan++;
-      if (scan < in->len && (!alternate) && in->ptr[scan] == '=')
-       {
-         is_keyword = 1;
-         if (is_positional)
-           {
-             ERROR ((stderr, "Can't mix positional and keyword arguments.\n"));
-             return;
-           }
-         /* This is a keyword arg, fetch the formal name and
-            then the actual stuff */
-         sb_reset (&t);
-         idx = get_token (idx, in, &t);
-         if (in->ptr[idx] != '=')
-           ERROR ((stderr, "confused about formal params.\n"));
-
-         /* Lookup the formal in the macro's list */
-         ptr = hash_lookup (&m->formal_hash, &t);
-         if (!ptr)
-           {
-             ERROR ((stderr, "MACRO formal argument %s does not exist.\n", sb_name (&t)));
-             return;
-           }
-         else
-           {
-             /* Insert this value into the right place */
-             sb_reset (&ptr->value.f->actual);
-             idx = get_any_string (idx + 1, in, &ptr->value.f->actual, 0, 0);
-             if (ptr->value.f->actual.len > 0)
-               ++narg;
-           }
-       }
-      else
-       {
-         /* This is a positional arg */
-         is_positional = 1;
-         if (is_keyword)
-           {
-             ERROR ((stderr, "Can't mix positional and keyword arguments.\n"));
-             return;
-           }
-         if (!f)
-           {
-             formal_entry **pf;
-             int c;
-
-             if (!mri)
-               {
-                 ERROR ((stderr, "Too many positional arguments.\n"));
-                 return;
-               }
-             f = (formal_entry *) xmalloc (sizeof (formal_entry));
-             sb_new (&f->name);
-             sb_new (&f->def);
-             sb_new (&f->actual);
-             f->next = NULL;
-
-             c = -1;
-             for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next)
-               if ((*pf)->index >= c)
-                 c = (*pf)->index + 1;
-             *pf = f;
-             f->index = c;
-           }
-
-         sb_reset (&f->actual);
-         idx = get_any_string (idx, in, &f->actual, 1, 0);
-         if (f->actual.len > 0)
-           ++narg;
-         do
-           {
-             f = f->next;
-           }
-         while (f != NULL && f->index < 0);
-       }
+  sb name;
 
-      idx = sb_skip_comma (idx, in);
-    }
+  if (! macro_defined)
+    return 0;
 
-  if (mri)
-    {
-      char buffer[20];
-
-      sb_reset (&t);
-      sb_add_string (&t, "NARG");
-      ptr = hash_lookup (&m->formal_hash, &t);
-      sb_reset (&ptr->value.f->actual);
-      sprintf (buffer, "%d", narg);
-      sb_add_string (&ptr->value.f->actual, buffer);
-    }
+  sb_terminate (in);
+  if (! check_macro (in->ptr + idx, &out, comment_char, &err))
+    return 0;
 
-  macro_expand_body (name, &m->sub, &out, m->formals, &m->formal_hash);
+  if (err != NULL)
+    ERROR ((stderr, "%s\n", err));
 
-  include_buf (name, &out, include_macro, include_next_index ());
+  sb_new (&name);
+  sb_add_string (&name, "macro expansion");
 
-  if (mri)
-    {
-      formal_entry **pf;
+  include_buf (&name, &out, include_macro, include_next_index ());
 
-      /* Discard any unnamed formal arguments.  */
-      pf = &m->formals;
-      while (*pf != NULL)
-       {
-         if ((*pf)->name.len != 0)
-           pf = &(*pf)->next;
-         else
-           {
-             sb_kill (&(*pf)->name);
-             sb_kill (&(*pf)->def);
-             sb_kill (&(*pf)->actual);
-             f = (*pf)->next;
-             free (*pf);
-             *pf = f;
-           }
-       }
-    }
-
-  sb_kill (&t);
+  sb_kill (&name);
   sb_kill (&out);
-  number++;
-}
-
-static int
-macro_op (idx, in)
-     int idx;
-     sb *in;
-{
-  int res = 0;
-  /* The macro name must be the first thing on the line */
-  if (idx < in->len)
-    {
-      sb name;
-      hash_entry *ptr;
-      sb_new (&name);
-      idx = get_token (idx, in, &name);
 
-      if (name.len)
-       {
-         /* Got a name, look it up */
-
-         ptr = hash_lookup (&macro_table, &name);
-
-         if (ptr)
-           {
-             /* It's in the table, copy out the stuff and convert any macro args */
-             macro_expand (&name, idx, in, ptr->value.m);
-             res = 1;
-           }
-       }
-      sb_kill (&name);
-    }
-
-
-  return res;
+  return 1;
 }
 
-
 /* STRING HANDLING */
 
 static int
@@ -3810,7 +2802,7 @@ do_sdatab (idx, in)
       if (i)
        fprintf (outfile, "\t");
       fprintf (outfile, ".byte\t");
-      sb_print (&acc);
+      sb_print (outfile, &acc);
       fprintf (outfile, "\n");
     }
   sb_kill (&acc);
@@ -4233,6 +3225,7 @@ process_pseudo_op (idx, line, acc)
            {
            case K_ALTERNATE:
              alternate = 1;
+             macro_init (1, mri, exp_get_abs);
              return 1;
            case K_AELSE:
              do_aelse ();
@@ -4543,7 +3536,6 @@ main (argc, argv)
   program_name = argv[0];
   xmalloc_set_program_name (program_name);
 
-  hash_new_table (101, &macro_table);
   hash_new_table (101, &keyword_hash_table);
   hash_new_table (101, &assign_hash_table);
   hash_new_table (101, &vars);
@@ -4613,6 +3605,8 @@ main (argc, argv)
 
   process_init ();
 
+  macro_init (alternate, mri, exp_get_abs);
+
   if (out_name) {
     outfile = fopen (out_name, "w");
     if (!outfile)
index bdfd91ca978bde9285b59b9fbd601aae6d297fb4..b495ae0640126467bd5b7a7d677bade622dfb195 100644 (file)
@@ -1,29 +1,30 @@
 /* input_scrub.c - Break up input buffers into whole numbers of lines.
    Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
-   
+
    This file is part of GAS, the GNU Assembler.
-   
+
    GAS is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2, or (at your option)
    any later version.
-   
+
    GAS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+   the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
 
-#include <errno.h>     /* Need this to make errno declaration right */
+#include <errno.h>             /* Need this to make errno declaration right */
 #include "as.h"
 #include "input-file.h"
+#include "sb.h"
 
 /*
  * O/S independent module to supply buffers of sanitised source code
- * to rest of assembler. We get sanitized input data of arbitrary length.
+ * to rest of assembler.  We get sanitised input data of arbitrary length.
  * We break these buffers on line boundaries, recombine pieces that
  * were broken across buffers, and return a buffer of full lines to
  * the caller.
 #define BEFORE_SIZE (1)
 #define AFTER_SIZE  (1)
 
-static char *  buffer_start;   /*->1st char of full buffer area. */
-static char *  partial_where;  /*->after last full line in buffer. */
+static char *buffer_start;     /*->1st char of full buffer area. */
+static char *partial_where;    /*->after last full line in buffer. */
 static int partial_size;       /* >=0. Number of chars in partial line in buffer. */
-static char save_source [AFTER_SIZE];
+static char save_source[AFTER_SIZE];
 /* Because we need AFTER_STRING just after last */
 /* full line, it clobbers 1st part of partial */
 /* line. So we preserve 1st part of partial */
 /* line here. */
-static int buffer_length;      /* What is the largest size buffer that */
+static unsigned int buffer_length;     /* What is the largest size buffer that */
 /* input_file_give_next_buffer() could */
 /* return to us? */
 
-/* Saved information about the file that .include'd this one.  When we hit EOF,
-   we automatically pop to that file. */
+/* The index into an sb structure we are reading from.  -1 if none.  */
+static int sb_index = -1;
+
+/* If we are reading from an sb structure, this is it.  */
+static sb from_sb;
 
-static char *next_saved_file;
+/* The number of nested sb structures we have included.  */
+static int macro_nest;
 
 /* We can have more than one source file open at once, though the info for all
    but the latest one are saved off in a struct input_save.  These files remain
@@ -80,140 +85,152 @@ static char *next_saved_file;
    source line numbers.  Whenever we open a file we must fill in
    physical_input_file. So if it is NULL we have not opened any files yet. */
 
-char *physical_input_file;
-char *logical_input_file;
+static char *physical_input_file;
+static char *logical_input_file;
 
-typedef unsigned int line_numberT; /* 1-origin line number in a source file. */
+typedef unsigned int line_numberT;     /* 1-origin line number in a source file. */
 /* A line ends in '\n' or eof. */
 
-line_numberT physical_input_line;
-line_numberT logical_input_line;
+static line_numberT physical_input_line;
+static int logical_input_line;
 
 /* Struct used to save the state of the input handler during include files */
-struct input_save {
-       char *buffer_start;
-       char *partial_where;
-       int partial_size;
-       char save_source [AFTER_SIZE];
-       int buffer_length;
-       char *physical_input_file;
-       char *logical_input_file;
-       line_numberT    physical_input_line;
-       line_numberT    logical_input_line;
-       char *next_saved_file;  /* Chain of input_saves */
-       char *input_file_save;  /* Saved state of input routines */
-       char *saved_position;   /* Caller's saved position in buf */
-};
-
-#if __STDC__ == 1
-static void as_1_char(unsigned int c, FILE *stream);
-#else /* __STDC__ */
-static void as_1_char();
-#endif /* not __STDC__ */
+struct input_save
+  {
+    char *buffer_start;
+    char *partial_where;
+    int partial_size;
+    char save_source[AFTER_SIZE];
+    unsigned int buffer_length;
+    char *physical_input_file;
+    char *logical_input_file;
+    line_numberT physical_input_line;
+    int logical_input_line;
+    int sb_index;
+    sb from_sb;
+    struct input_save *next_saved_file;        /* Chain of input_saves */
+    char *input_file_save;     /* Saved state of input routines */
+    char *saved_position;      /* Caller's saved position in buf */
+  };
+
+static struct input_save *input_scrub_push PARAMS ((char *saved_position));
+static char *input_scrub_pop PARAMS ((struct input_save *arg));
+static void as_1_char PARAMS ((unsigned int c, FILE * stream));
+
+/* Saved information about the file that .include'd this one.  When we hit EOF,
+   we automatically pop to that file. */
+
+static struct input_save *next_saved_file;
 
 /* Push the state of input reading and scrubbing so that we can #include.
    The return value is a 'void *' (fudged for old compilers) to a save
    area, which can be restored by passing it to input_scrub_pop(). */
-char *input_scrub_push(saved_position)
-char *saved_position;
+static struct input_save *
+input_scrub_push (saved_position)
+     char *saved_position;
 {
-       register struct input_save *saved;
-       
-       saved = (struct input_save *) xmalloc(sizeof *saved);
-       
-       saved->saved_position           = saved_position;
-       saved->buffer_start             = buffer_start;
-       saved->partial_where            = partial_where;
-       saved->partial_size             = partial_size;
-       saved->buffer_length            = buffer_length;
-       saved->physical_input_file      = physical_input_file;
-       saved->logical_input_file       = logical_input_file;
-       saved->physical_input_line      = physical_input_line;
-       saved->logical_input_line       = logical_input_line;
-       memcpy(saved->save_source, save_source, sizeof(save_source));
-       saved->next_saved_file          = next_saved_file;
-       saved->input_file_save          = input_file_push();
-       
-       input_file_begin();             /* Reinitialize! */
-       logical_input_line = 0;
-       logical_input_file = (char *)NULL;
-       
-       return((char *) saved);
-} /* input_scrub_push() */
-
-char *
-    input_scrub_pop (arg)
-char *arg;
+  register struct input_save *saved;
+
+  saved = (struct input_save *) xmalloc (sizeof *saved);
+
+  saved->saved_position = saved_position;
+  saved->buffer_start = buffer_start;
+  saved->partial_where = partial_where;
+  saved->partial_size = partial_size;
+  saved->buffer_length = buffer_length;
+  saved->physical_input_file = physical_input_file;
+  saved->logical_input_file = logical_input_file;
+  saved->physical_input_line = physical_input_line;
+  saved->logical_input_line = logical_input_line;
+  saved->sb_index = sb_index;
+  saved->from_sb = from_sb;
+  memcpy (saved->save_source, save_source, sizeof (save_source));
+  saved->next_saved_file = next_saved_file;
+  saved->input_file_save = input_file_push ();
+
+  input_file_begin ();         /* Reinitialize! */
+  logical_input_line = -1;
+  logical_input_file = (char *) NULL;
+  buffer_length = input_file_buffer_size ();
+
+  buffer_start = xmalloc ((BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE));
+  memcpy (buffer_start, BEFORE_STRING, (int) BEFORE_SIZE);
+
+  return saved;
+}                              /* input_scrub_push() */
+
+static char *
+input_scrub_pop (saved)
+     struct input_save *saved;
 {
-       register struct input_save *saved;
-       char *saved_position;
-       
-       input_scrub_end ();     /* Finish off old buffer */
-       
-       saved = (struct input_save *)arg;
-       
-       input_file_pop           (saved->input_file_save);
-       saved_position          = saved->saved_position;
-       buffer_start            = saved->buffer_start;
-       buffer_length           = saved->buffer_length;
-       physical_input_file     = saved->physical_input_file;
-       logical_input_file      = saved->logical_input_file;
-       physical_input_line     = saved->physical_input_line;
-       logical_input_line      = saved->logical_input_line;
-       partial_where           = saved->partial_where;
-       partial_size            = saved->partial_size;
-       next_saved_file         = saved->next_saved_file;
-       memcpy(save_source, saved->save_source, sizeof (save_source));
-       
-       free(arg);
-       return saved_position;
+  char *saved_position;
+
+  input_scrub_end ();          /* Finish off old buffer */
+
+  input_file_pop (saved->input_file_save);
+  saved_position = saved->saved_position;
+  buffer_start = saved->buffer_start;
+  buffer_length = saved->buffer_length;
+  physical_input_file = saved->physical_input_file;
+  logical_input_file = saved->logical_input_file;
+  physical_input_line = saved->physical_input_line;
+  logical_input_line = saved->logical_input_line;
+  sb_index = saved->sb_index;
+  from_sb = saved->from_sb;
+  partial_where = saved->partial_where;
+  partial_size = saved->partial_size;
+  next_saved_file = saved->next_saved_file;
+  memcpy (save_source, saved->save_source, sizeof (save_source));
+
+  free (saved);
+  return saved_position;
 }
-
 \f
+
 void
-    input_scrub_begin ()
+input_scrub_begin ()
 {
-       know(strlen(BEFORE_STRING) == BEFORE_SIZE);
-       know(strlen(AFTER_STRING) ==  AFTER_SIZE || (AFTER_STRING[0] == '\0' && AFTER_SIZE == 1));
-       
-       input_file_begin ();
-       
-       buffer_length = input_file_buffer_size ();
-       
-       buffer_start = xmalloc((long)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE));
-       memcpy(buffer_start, BEFORE_STRING, (int) BEFORE_SIZE);
-       
-       /* Line number things. */
-       logical_input_line = 0;
-       logical_input_file = (char *)NULL;
-       physical_input_file = NULL;     /* No file read yet. */
-       next_saved_file = NULL; /* At EOF, don't pop to any other file */
-       do_scrub_begin();
+  know (strlen (BEFORE_STRING) == BEFORE_SIZE);
+  know (strlen (AFTER_STRING) == AFTER_SIZE || (AFTER_STRING[0] == '\0' && AFTER_SIZE == 1));
+
+  input_file_begin ();
+
+  buffer_length = input_file_buffer_size ();
+
+  buffer_start = xmalloc ((BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE));
+  memcpy (buffer_start, BEFORE_STRING, (int) BEFORE_SIZE);
+
+  /* Line number things. */
+  logical_input_line = -1;
+  logical_input_file = (char *) NULL;
+  physical_input_file = NULL;  /* No file read yet. */
+  next_saved_file = NULL;      /* At EOF, don't pop to any other file */
+  do_scrub_begin ();
 }
 
 void
-    input_scrub_end ()
+input_scrub_end ()
 {
-       if (buffer_start)
-           {
-                   free (buffer_start);
-                   buffer_start = 0;
-                   input_file_end ();
-           }
+  if (buffer_start)
+    {
+      free (buffer_start);
+      buffer_start = 0;
+      input_file_end ();
+    }
 }
 
 /* Start reading input from a new file. */
 
 char *                         /* Return start of caller's part of buffer. */
-    input_scrub_new_file (filename)
-char * filename;
+input_scrub_new_file (filename)
+     char *filename;
 {
-       input_file_open (filename, !flagseen['f']);
-       physical_input_file = filename[0] ? filename : "{standard input}";
-       physical_input_line = 0;
-       
-       partial_size = 0;
-       return (buffer_start + BEFORE_SIZE);
+  input_file_open (filename, !flag_no_comments);
+  physical_input_file = filename[0] ? filename : "{standard input}";
+  physical_input_line = 0;
+
+  partial_size = 0;
+  return (buffer_start + BEFORE_SIZE);
 }
 
 
@@ -222,94 +239,113 @@ char *   filename;
    input_scrub_new_file. */
 
 char *
-    input_scrub_include_file (filename, position)
-char *filename;
-char *position;
+input_scrub_include_file (filename, position)
+     char *filename;
+     char *position;
 {
-       next_saved_file = input_scrub_push(position);
-       return input_scrub_new_file (filename);
+  next_saved_file = input_scrub_push (position);
+  return input_scrub_new_file (filename);
 }
 
+/* Start getting input from an sb structure.  This is used when
+   expanding a macro.  */
+
 void
-    input_scrub_close ()
+input_scrub_include_sb (from, position)
+     sb *from;
+     char *position;
 {
-       input_file_close ();
+  if (macro_nest > max_macro_nest)
+    as_fatal ("macros nested too deeply");
+  ++macro_nest;
+
+  next_saved_file = input_scrub_push (position);
+
+  sb_new (&from_sb);
+  /* Add the sentinel required by read.c.  */
+  sb_add_char (&from_sb, '\n');
+  sb_add_sb (&from_sb, from);
+  sb_index = 1;
+
+  /* These variables are reset by input_scrub_push.  Restore them
+     since we are, after all, still at the same point in the file.  */
+  logical_input_line = next_saved_file->logical_input_line;
+  logical_input_file = next_saved_file->logical_input_file;
 }
+
+void
+input_scrub_close ()
+{
+  input_file_close ();
+}
+
 char *
-    input_scrub_next_buffer (bufp)
-char **bufp;
+input_scrub_next_buffer (bufp)
+     char **bufp;
 {
-       register char * limit;  /*->just after last char of buffer. */
-       
-       *bufp = buffer_start + BEFORE_SIZE;
-       
-#ifdef DONTDEF
-       if(preprocess) {
-               if(save_buffer) {
-                       *bufp = save_buffer;
-                       save_buffer = 0;
-               }
-               limit = input_file_give_next_buffer(buffer_start+BEFORE_SIZE);
-               if (!limit) {
-                       partial_where = 0;
-                       if(partial_size)
-                           as_warn("Partial line at end of file ignored");
-                       return partial_where;
-               }
-               
-               if(partial_size)
-                   memcpy(partial_where, save_source, (int) AFTER_SIZE);
-               do_scrub(partial_where,partial_size,buffer_start+BEFORE_SIZE,limit-(buffer_start+BEFORE_SIZE),&out_string,&out_length);
-               limit=out_string + out_length;
-               for(p=limit;*--p!='\n';)
-                   ;
-               p++;
-               if(p<=buffer_start+BEFORE_SIZE)
-                   as_fatal("Source line too long.  Please change file '%s' and re-make the assembler.", __FILE__);
-               
-               partial_where = p;
-               partial_size = limit-p;
-               memcpy(save_source, partial_where, (int) AFTER_SIZE);
-               memcpy(partial_where, AFTER_STRING, (int) AFTER_SIZE);
-               
-               save_buffer = *bufp;
-               *bufp = out_string;
-               
-               return partial_where;
+  register char *limit;                /*->just after last char of buffer. */
+
+  if (sb_index >= 0)
+    {
+      if (sb_index >= from_sb.len)
+       {
+         sb_kill (&from_sb);
+         --macro_nest;
+         partial_where = NULL;
+         if (next_saved_file != NULL)
+           *bufp = input_scrub_pop (next_saved_file);
+         return partial_where;
        }
-       
-       /* We're not preprocessing.  Do the right thing */
-#endif
-       if (partial_size) {
-               memcpy(buffer_start + BEFORE_SIZE, partial_where, (int) partial_size);
-               memcpy(buffer_start + BEFORE_SIZE, save_source, (int) AFTER_SIZE);
+
+      partial_where = from_sb.ptr + from_sb.len;
+      partial_size = 0;
+      *bufp = from_sb.ptr + sb_index;
+      sb_index = from_sb.len;
+      return partial_where;
+    }
+
+  *bufp = buffer_start + BEFORE_SIZE;
+
+  if (partial_size)
+    {
+      memcpy (buffer_start + BEFORE_SIZE, partial_where,
+             (unsigned int) partial_size);
+      memcpy (buffer_start + BEFORE_SIZE, save_source, AFTER_SIZE);
+    }
+  limit = input_file_give_next_buffer (buffer_start
+                                      + BEFORE_SIZE
+                                      + partial_size);
+  if (limit)
+    {
+      register char *p;                /* Find last newline. */
+
+      for (p = limit; *--p != '\n';);;
+      ++p;
+      if (p <= buffer_start + BEFORE_SIZE)
+       {
+         as_fatal ("Source line too long. Please change file %s then rebuild assembler.", __FILE__);
+       }
+      partial_where = p;
+      partial_size = limit - p;
+      memcpy (save_source, partial_where, (int) AFTER_SIZE);
+      memcpy (partial_where, AFTER_STRING, (int) AFTER_SIZE);
+    }
+  else
+    {
+      partial_where = 0;
+      if (partial_size > 0)
+       {
+         as_warn ("Partial line at end of file ignored");
        }
-       limit = input_file_give_next_buffer (buffer_start + BEFORE_SIZE + partial_size);
-       if (limit) {
-               register char * p;      /* Find last newline. */
-               
-               for (p = limit; *--p != '\n';) ;;
-               ++p;
-               if (p <= buffer_start + BEFORE_SIZE) {
-                       as_fatal("Source line too long. Please change file %s then rebuild assembler.", __FILE__);
-               }
-               partial_where = p;
-               partial_size = limit - p;
-               memcpy(save_source, partial_where, (int) AFTER_SIZE);
-               memcpy(partial_where, AFTER_STRING, (int) AFTER_SIZE);
-       } else {
-               partial_where = 0;
-               if (partial_size > 0) {
-                       as_warn("Partial line at end of file ignored");
-               }
-               /* If we should pop to another file at EOF, do it. */
-               if (next_saved_file) {
-                       *bufp = input_scrub_pop (next_saved_file);      /* Pop state */
-                       /* partial_where is now correct to return, since we popped it. */
-               }
+      /* If we should pop to another file at EOF, do it. */
+      if (next_saved_file)
+       {
+         *bufp = input_scrub_pop (next_saved_file);    /* Pop state */
+         /* partial_where is now correct to return, since we popped it. */
        }
-       return(partial_where);
-} /* input_scrub_next_buffer() */
+    }
+  return (partial_where);
+}                              /* input_scrub_next_buffer() */
 \f
 /*
  * The remaining part of this file deals with line numbers, error
@@ -318,83 +354,84 @@ char **bufp;
 
 
 int
-    seen_at_least_1_file ()            /* TRUE if we opened any file. */
+seen_at_least_1_file ()                /* TRUE if we opened any file. */
 {
-       return (physical_input_file != NULL);
+  return (physical_input_file != NULL);
 }
 
 void
-    bump_line_counters ()
+bump_line_counters ()
 {
-       ++ physical_input_line;
-       /*  ++ logical_input_line; FIXME-now remove this. */
+  if (sb_index < 0)
+    {
+      ++physical_input_line;
+      if (logical_input_line >= 0)
+       ++logical_input_line;
+    }
 }
 \f
 /*
  *                     new_logical_line()
  *
  * Tells us what the new logical line number and file are.
- * If the line_number is <0, we don't change the current logical line number.
+ * If the line_number is -1, we don't change the current logical line
+ * number.  If it is -2, we decrement the logical line number (this is
+ * to support the .appfile pseudo-op inserted into the stream by
+ * do_scrub_next_char).
  * If the fname is NULL, we don't change the current logical file name.
  */
-void new_logical_line(fname, line_number)
-char *fname;           /* DON'T destroy it! We point to it! */
-int line_number;
+void 
+new_logical_line (fname, line_number)
+     char *fname;              /* DON'T destroy it! We point to it! */
+     int line_number;
 {
-       if (fname) {
-               logical_input_file = fname;
-       } /* if we have a file name */
-       
-       if (line_number >= 0) {
-               logical_input_line = line_number;
-       } /* if we have a line number */
-} /* new_logical_line() */
+  if (fname)
+    {
+      logical_input_file = fname;
+    }                          /* if we have a file name */
+
+  if (line_number >= 0)
+    logical_input_line = line_number;
+  else if (line_number == -2 && logical_input_line > 0)
+    --logical_input_line;
+}                              /* new_logical_line() */
 \f
 /*
  *                     a s _ w h e r e ()
  *
- * Write a line to stderr locating where we are in reading
- * input source files.
- * As a sop to the debugger of AS, pretty-print the offending line.
+ * Return the current file name and line number.
+ * namep should be char * const *, but there are compilers which screw
+ * up declarations like that, and it's easier to avoid it.
  */
-void as_where() {
-       char *p;
-       line_numberT line;
-       
-       if (logical_input_file && (logical_input_line > 0)) {
-               p = logical_input_file;
-               line = logical_input_line;
-       } else {
-               p = physical_input_file;
-               line = physical_input_line;
-       } /* line number should match file name */
-       
-       fprintf(stderr, "%s:%u: ", p, line);
-       
-#ifdef DONTDEF
-       if (physical_input_file) {
-               if (input_file_is_open()) { /* we can still read lines from source */
-                       fprintf (stderr," @ physical line %ld., file \"%s\"",
-                                (long) physical_input_line, physical_input_file);
-                       fprintf (stderr," @ logical line %ld., file \"%s\"\n",
-                                (long) logical_input_line, logical_input_file);
-                       (void)putc(' ', stderr);
-                       as_howmuch (stderr);
-                       (void)putc('\n', stderr);
-               } else {
-                       fprintf(stderr, " After reading source.\n");
-               } /* input file is still open */
-       } else {
-               fprintf(stderr, " Before reading source.\n");
-       } /* we tried to read SOME source */
-#endif /* DONTDEF */
-       
-       return;
-} /* as_where() */
+void 
+as_where (namep, linep)
+     char **namep;
+     unsigned int *linep;
+{
+  if (logical_input_file != NULL
+      && (linep == NULL || logical_input_line >= 0))
+    {
+      *namep = logical_input_file;
+      if (linep != NULL)
+       *linep = logical_input_line;
+    }
+  else if (physical_input_file != NULL)
+    {
+      *namep = physical_input_file;
+      if (linep != NULL)
+       *linep = physical_input_line;
+    }
+  else
+    {
+      *namep = (char *) "*unknown*";
+      if (linep != NULL)
+       *linep = 0;
+    }
+}                              /* as_where() */
+\f
 
 
 
-\f
 /*
  *                     a s _ h o w m u c h ()
  *
@@ -403,46 +440,40 @@ void as_where() {
  * No free '\n' at end of line.
  */
 void
-    as_howmuch (stream)
-FILE * stream;         /* Opened for write please. */
+as_howmuch (stream)
+     FILE *stream;             /* Opened for write please. */
 {
-       register char * p;      /* Scan input line. */
-       /* register char c; JF unused */
-       
-       for (p = input_line_pointer - 1;   * p != '\n';   --p)
-           {
-           }
-       ++ p;                           /* p->1st char of line. */
-       for (;  p <= input_line_pointer;  p++)
-           {
-                   /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */
-                   /* c = *p & 0xFF; JF unused */
-                   as_1_char(*p, stream);
-           }
+  register char *p;            /* Scan input line. */
+  /* register char c; JF unused */
+
+  for (p = input_line_pointer - 1; *p != '\n'; --p)
+    {
+    }
+  ++p;                         /* p->1st char of line. */
+  for (; p <= input_line_pointer; p++)
+    {
+      /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */
+      /* c = *p & 0xFF; JF unused */
+      as_1_char ((unsigned char) *p, stream);
+    }
 }
 
-static void as_1_char (c,stream)
-unsigned int c;
-FILE *stream;
+static void 
+as_1_char (c, stream)
+     unsigned int c;
+     FILE *stream;
 {
-       if (c > 127)
-           {
-                   (void)putc('%', stream);
-                   c -= 128;
-           }
-       if (c < 32)
-           {
-                   (void)putc('^', stream);
-                   c += '@';
-           }
-       (void)putc(c, stream);
+  if (c > 127)
+    {
+      (void) putc ('%', stream);
+      c -= 128;
+    }
+  if (c < 32)
+    {
+      (void) putc ('^', stream);
+      c += '@';
+    }
+  (void) putc (c, stream);
 }
 
-/*
- * Local Variables:
- * comment-column: 0
- * fill-column: 131
- * End:
- */
-
 /* end of input_scrub.c */
diff --git a/gas/macro.h b/gas/macro.h
new file mode 100644 (file)
index 0000000..7a4c4eb
--- /dev/null
@@ -0,0 +1,46 @@
+/* macro.h - header file for macro support for gas and gasp
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+
+   Written by Steve and Judy Chamberlain of Cygnus Support,
+      sac@cygnus.com
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef MACRO_H
+
+#define MACRO_H
+
+#include "ansidecl.h"
+#include "sb.h"
+
+/* Whether any macros have been defined.  */
+
+extern int macro_defined;
+
+extern int buffer_and_nest
+  PARAMS ((const char *, const char *, sb *, int (*) PARAMS ((sb *))));
+extern void macro_init
+  PARAMS ((int alternate, int mri,
+          int (*) PARAMS ((const char *, int, sb *, int *))));
+extern const char *define_macro
+  PARAMS ((int idx, sb *in, sb *label, int (*get_line) PARAMS ((sb *))));
+extern int check_macro PARAMS ((const char *, sb *, int, const char **));
+extern const char *expand_irp
+  PARAMS ((int, int, sb *, sb *, int (*) PARAMS ((sb *)), int));
+
+#endif
index 7a9cd3e4943541b887ede73dcc9eff18f6c7678e..a117b757364f377f1ff44e59e5cb7a46119039b8 100644 (file)
@@ -42,6 +42,8 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307
 
 #include "as.h"
 #include "subsegs.h"
+#include "sb.h"
+#include "macro.h"
 #include "libiberty.h"
 #include "obstack.h"
 #include "listing.h"
@@ -176,7 +178,7 @@ addressT abs_section_offset;
 
 /* If this line had an MRI style label, it is stored in this variable.
    This is used by some of the MRI pseudo-ops.  */
-symbolS *mri_line_label;
+symbolS *line_label;
 
 /* This global variable is used to support MRI common sections.  We
    translate such sections into a common symbol.  This variable is
@@ -197,6 +199,7 @@ int is_it_end_of_statement PARAMS ((void));
 static segT get_segmented_expression PARAMS ((expressionS *expP));
 static segT get_known_segmented_expression PARAMS ((expressionS * expP));
 static void pobegin PARAMS ((void));
+static int get_line_sb PARAMS ((sb *));
 \f
 
 void
@@ -275,6 +278,7 @@ static const pseudo_typeS potable[] =
 /* endef */
   {"equ", s_set, 0},
 /* err */
+  {"exitm", s_mexit, 0},
 /* extend */
   {"extern", s_ignore, 0},     /* We treat all undef as ext */
   {"appfile", s_app_file, 1},
@@ -288,6 +292,7 @@ static const pseudo_typeS potable[] =
   {"globl", s_globl, 0},
   {"hword", cons, 2},
   {"if", s_if, (int) O_ne},
+  {"ifc", s_ifc, 0},
   {"ifdef", s_ifdef, 0},
   {"ifeq", s_if, (int) O_eq},
   {"ifeqs", s_ifeqs, 0},
@@ -295,18 +300,23 @@ static const pseudo_typeS potable[] =
   {"ifgt", s_if, (int) O_gt},
   {"ifle", s_if, (int) O_le},
   {"iflt", s_if, (int) O_lt},
+  {"ifnc", s_ifc, 1},
   {"ifndef", s_ifdef, 1},
   {"ifne", s_if, (int) O_ne},
   {"ifnes", s_ifeqs, 1},
   {"ifnotdef", s_ifdef, 1},
   {"include", s_include, 0},
   {"int", cons, 4},
+  {"irp", s_irp, 0},
+  {"irpc", s_irp, 1},
   {"lcomm", s_lcomm, 0},
   {"lflags", listing_flags, 0},        /* Listing flags */
   {"list", listing_list, 1},   /* Turn listing on */
   {"llen", listing_psize, 1},
   {"long", cons, 4},
   {"lsym", s_lsym, 0},
+  {"macro", s_macro, 0},
+  {"mexit", s_mexit, 0},
   {"noformat", s_ignore, 0},
   {"nolist", listing_list, 0}, /* Turn listing off */
   {"nopage", listing_nopage, 0},
@@ -319,6 +329,7 @@ static const pseudo_typeS potable[] =
   {"psize", listing_psize, 0}, /* set paper size */
 /* print */
   {"quad", cons, 8},
+  {"rept", s_rept, 0},
   {"sbttl", listing_title, 1}, /* Subtitle of listing */
 /* scl */
 /* sect */
@@ -457,43 +468,47 @@ read_a_source_file (name)
              if (input_line_pointer[-1] == '\n')
                bump_line_counters ();
 
+             line_label = NULL;
+
              if (flag_mri
 #ifdef LABELS_WITHOUT_COLONS
                  || 1
 #endif
                  )
                {
-                 mri_line_label = NULL;
-
                  /* Text at the start of a line must be a label, we
                     run down and stick a colon in.  */
                  if (is_name_beginner (*input_line_pointer))
                    {
                      char *line_start = input_line_pointer;
-                     char c = get_symbol_end ();
+                     char c;
+
+                     HANDLE_CONDITIONAL_ASSEMBLY ();
 
-                     if (! ignore_input ())
+                     c = get_symbol_end ();
+
+                     /* In MRI mode, the EQU pseudoop must be
+                        handled specially.  */
+                     if (flag_mri)
                        {
-                         /* In MRI mode, the EQU pseudoop must be
-                            handled specially.  */
-                         if (flag_mri)
+                         char *rest = input_line_pointer + 1;
+
+                         if (*rest == ':')
+                           ++rest;
+                         if (*rest == ' ' || *rest == '\t')
+                           ++rest;
+                         if ((strncasecmp (rest, "EQU", 3) == 0
+                              || strncasecmp (rest, "SET", 3) == 0)
+                             && (rest[3] == ' ' || rest[3] == '\t'))
                            {
-                             if (((strncasecmp (input_line_pointer + 1,
-                                                "EQU", 3) == 0)
-                                  || (strncasecmp (input_line_pointer + 1,
-                                                   "SET", 3) == 0))
-                                 && (input_line_pointer[4] == ' '
-                                     || input_line_pointer[4] == '\t'))
-                               {
-                                 input_line_pointer += 4;
-                                 equals (line_start);
-                                 continue;
-                               }
+                             input_line_pointer = rest + 3;
+                             equals (line_start);
+                             continue;
                            }
-
-                         mri_line_label = colon (line_start);
                        }
 
+                     line_label = colon (line_start);
+
                      *input_line_pointer = c;
                      if (c == ':')
                        input_line_pointer++;
@@ -544,7 +559,27 @@ read_a_source_file (name)
               */
              if (TC_START_LABEL(c, input_line_pointer))
                {
-                 colon (s);    /* user-defined label */
+                 if (flag_mri)
+                   {
+                     char *rest = input_line_pointer + 1;
+
+                     /* In MRI mode, \tsym: set 0 is permitted.  */
+
+                     if (*rest == ':')
+                       ++rest;
+                     if (*rest == ' ' || *rest == '\t')
+                       ++rest;
+                     if ((strncasecmp (rest, "EQU", 3) == 0
+                          || strncasecmp (rest, "SET", 3) == 0)
+                         && (rest[3] == ' ' || rest[3] == '\t'))
+                       {
+                         input_line_pointer = rest + 3;
+                         equals (s);
+                         continue;
+                       }
+                   }
+
+                 line_label = colon (s);       /* user-defined label */
                  *input_line_pointer++ = ':';  /* Put ':' back for error messages' sake. */
                  /* Input_line_pointer->after ':'. */
                  SKIP_WHITESPACE ();
@@ -591,7 +626,8 @@ read_a_source_file (name)
                        pop = NULL;
                    }
 
-                 if (pop != NULL || *s == '.')
+                 if (pop != NULL
+                     || (! flag_mri && *s == '.'))
                    {
                      /*
                       * PSEUDO - OP.
@@ -684,6 +720,25 @@ read_a_source_file (name)
                        }
 #endif
 
+                     if (macro_defined)
+                       {
+                         sb out;
+                         const char *err;
+
+                         if (check_macro (s, &out, '\0', &err))
+                           {
+                             if (err != NULL)
+                               as_bad (err);
+                             *input_line_pointer++ = c;
+                             input_scrub_include_sb (&out,
+                                                     input_line_pointer);
+                             sb_kill (&out);
+                             buffer_limit =
+                               input_scrub_next_buffer (&input_line_pointer);
+                             continue;
+                           }
+                       }
+
                      md_assemble (s);  /* Assemble 1 instruction. */
 
                      *input_line_pointer++ = c;
@@ -1076,12 +1131,12 @@ s_mri_common (small)
       c = *input_line_pointer;
       *input_line_pointer = '\0';
 
-      if (mri_line_label != NULL)
+      if (line_label != NULL)
        {
-         alc = (char *) xmalloc (strlen (S_GET_NAME (mri_line_label))
+         alc = (char *) xmalloc (strlen (S_GET_NAME (line_label))
                                  + (input_line_pointer - name)
                                  + 1);
-         sprintf (alc, "%s%s", name, S_GET_NAME (mri_line_label));
+         sprintf (alc, "%s%s", name, S_GET_NAME (line_label));
          name = alc;
        }
     }
@@ -1119,13 +1174,13 @@ s_mri_common (small)
     S_SET_ALIGN (sym, align);
 #endif
 
-  if (mri_line_label != NULL)
+  if (line_label != NULL)
     {
-      mri_line_label->sy_value.X_op = O_symbol;
-      mri_line_label->sy_value.X_add_symbol = sym;
-      mri_line_label->sy_value.X_add_number = S_GET_VALUE (sym);
-      mri_line_label->sy_frag = &zero_address_frag;
-      S_SET_SEGMENT (mri_line_label, expr_section);
+      line_label->sy_value.X_op = O_symbol;
+      line_label->sy_value.X_add_symbol = sym;
+      line_label->sy_value.X_add_number = S_GET_VALUE (sym);
+      line_label->sy_frag = &zero_address_frag;
+      S_SET_SEGMENT (line_label, expr_section);
     }
 
   /* FIXME: We just ignore the small argument, which distinguishes
@@ -1345,6 +1400,37 @@ s_globl (ignore)
   demand_empty_rest_of_line ();
 }
 
+/* Handle the MRI IRP and IRPC pseudo-ops.  */
+
+void
+s_irp (irpc)
+     int irpc;
+{
+  char *file;
+  unsigned int line;
+  sb s;
+  const char *err;
+  sb out;
+
+  as_where (&file, &line);
+
+  sb_new (&s);
+  while (! is_end_of_line[(unsigned char) *input_line_pointer])
+    sb_add_char (&s, *input_line_pointer++);
+
+  sb_new (&out);
+
+  err = expand_irp (irpc, 0, &s, &out, get_line_sb, '\0');
+  if (err != NULL)
+    as_bad_where (file, line, "%s", err);
+
+  sb_kill (&s);
+
+  input_scrub_include_sb (&out, input_line_pointer);
+  sb_kill (&out);
+  buffer_limit = input_scrub_next_buffer (&input_line_pointer);
+}
+
 void 
 s_lcomm (needs_align)
      /* 1 if this was a ".bss" directive, which may require a 3rd argument
@@ -1569,6 +1655,84 @@ s_lsym (ignore)
   demand_empty_rest_of_line ();
 }                              /* s_lsym() */
 
+/* Read a line into an sb.  */
+
+static int
+get_line_sb (line)
+     sb *line;
+{
+  if (input_line_pointer >= buffer_limit)
+    {
+      buffer_limit = input_scrub_next_buffer (&input_line_pointer);
+      if (buffer_limit == 0)
+       return 0;
+    }
+
+  while (! is_end_of_line[(unsigned char) *input_line_pointer])
+    sb_add_char (line, *input_line_pointer++);
+  while (is_end_of_line[(unsigned char) *input_line_pointer])
+    {
+      if (*input_line_pointer == '\n')
+       {
+         bump_line_counters ();
+         LISTING_NEWLINE ();
+       }
+      ++input_line_pointer;
+    }
+  return 1;
+}
+
+/* Define a macro.  This is an interface to macro.c, which is shared
+   between gas and gasp.  */
+
+void
+s_macro (ignore)
+     int ignore;
+{
+  char *file;
+  unsigned int line;
+  sb s;
+  sb label;
+  const char *err;
+
+  as_where (&file, &line);
+
+  sb_new (&s);
+  while (! is_end_of_line[(unsigned char) *input_line_pointer])
+    sb_add_char (&s, *input_line_pointer++);
+
+  sb_new (&label);
+  if (line_label != NULL)
+    sb_add_string (&label, S_GET_NAME (line_label));
+
+  demand_empty_rest_of_line ();
+
+  err = define_macro (0, &s, &label, get_line_sb);
+  if (err != NULL)
+    as_bad_where (file, line, "%s", err);
+  else
+    {
+      if (line_label != NULL)
+       {
+         S_SET_SEGMENT (line_label, undefined_section);
+         S_SET_VALUE (line_label, 0);
+         line_label->sy_frag = &zero_address_frag;
+       }
+    }
+
+  sb_kill (&s);
+}
+
+/* Handle the .mexit pseudo-op, which immediately exits a macro
+   expansion.  */
+
+void
+s_mexit (ignore)
+     int ignore;
+{
+  buffer_limit = input_scrub_next_buffer (&input_line_pointer);
+}
+
 /* Handle changing the location counter.  */
 
 static void
@@ -1735,6 +1899,36 @@ s_mri_sect (type)
   demand_empty_rest_of_line ();
 }
 
+/* Handle the .rept pseudo-op.  */
+
+void
+s_rept (ignore)
+     int ignore;
+{
+  int count;
+  sb one;
+  sb many;
+
+  count = get_absolute_expression ();
+
+  sb_new (&one);
+  if (! buffer_and_nest ("REPT", "ENDR", &one, get_line_sb))
+    {
+      as_bad ("rept without endr");
+      return;
+    }
+
+  sb_new (&many);
+  while (count-- > 0)
+    sb_add_sb (&many, &one);
+
+  sb_kill (&one);
+
+  input_scrub_include_sb (&many, input_line_pointer);
+  sb_kill (&many);
+  buffer_limit = input_scrub_next_buffer (&input_line_pointer);
+}
+
 void 
 s_set (ignore)
      int ignore;
diff --git a/gas/sb.c b/gas/sb.c
new file mode 100644 (file)
index 0000000..b48b076
--- /dev/null
+++ b/gas/sb.c
@@ -0,0 +1,283 @@
+/* sb.c - string buffer manipulation routines
+   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+
+   Written by Steve and Judy Chamberlain of Cygnus Support,
+      sac@cygnus.com
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include "config.h"
+#include <stdio.h>
+#include "sb.h"
+
+/* These routines are about manipulating strings.
+
+   They are managed in things called `sb's which is an abbreviation
+   for string buffers.  An sb has to be created, things can be glued
+   on to it, and at the end of it's life it should be freed.  The
+   contents should never be pointed at whilst it is still growing,
+   since it could be moved at any time
+
+   eg:
+   sb_new (&foo);
+   sb_grow... (&foo,...);
+   use foo->ptr[*];
+   sb_kill (&foo);
+
+*/
+
+#define dsize 5
+
+static void sb_check PARAMS ((sb *, int));
+
+/* Statistics of sb structures.  */
+
+int string_count[sb_max_power_two];
+
+/* Free list of sb structures.  */
+
+static sb_list_vector free_list;
+
+/* initializes an sb. */
+
+void
+sb_build (ptr, size)
+     sb *ptr;
+     int size;
+{
+  /* see if we can find one to allocate */
+  sb_element *e;
+
+  if (size > sb_max_power_two)
+    abort ();
+
+  e = free_list.size[size];
+  if (!e)
+    {
+      /* nothing there, allocate one and stick into the free list */
+      e = (sb_element *) xmalloc (sizeof (sb_element) + (1 << size));
+      e->next = free_list.size[size];
+      e->size = 1 << size;
+      free_list.size[size] = e;
+      string_count[size]++;
+    }
+
+  /* remove from free list */
+
+  free_list.size[size] = e->next;
+
+  /* copy into callers world */
+  ptr->ptr = e->data;
+  ptr->pot = size;
+  ptr->len = 0;
+  ptr->item = e;
+}
+
+
+void
+sb_new (ptr)
+     sb *ptr;
+{
+  sb_build (ptr, dsize);
+}
+
+/* deallocate the sb at ptr */
+
+void
+sb_kill (ptr)
+     sb *ptr;
+{
+  /* return item to free list */
+  ptr->item->next = free_list.size[ptr->pot];
+  free_list.size[ptr->pot] = ptr->item;
+}
+
+/* add the sb at s to the end of the sb at ptr */
+
+void
+sb_add_sb (ptr, s)
+     sb *ptr;
+     sb *s;
+{
+  sb_check (ptr, s->len);
+  memcpy (ptr->ptr + ptr->len, s->ptr, s->len);
+  ptr->len += s->len;
+}
+
+/* make sure that the sb at ptr has room for another len characters,
+   and grow it if it doesn't. */
+
+static void
+sb_check (ptr, len)
+     sb *ptr;
+     int len;
+{
+  if (ptr->len + len >= 1 << ptr->pot)
+    {
+      sb tmp;
+      int pot = ptr->pot;
+      while (ptr->len + len >= 1 << pot)
+       pot++;
+      sb_build (&tmp, pot);
+      sb_add_sb (&tmp, ptr);
+      sb_kill (ptr);
+      *ptr = tmp;
+    }
+}
+
+/* make the sb at ptr point back to the beginning.  */
+
+void
+sb_reset (ptr)
+     sb *ptr;
+{
+  ptr->len = 0;
+}
+
+/* add character c to the end of the sb at ptr. */
+
+void
+sb_add_char (ptr, c)
+     sb *ptr;
+     int c;
+{
+  sb_check (ptr, 1);
+  ptr->ptr[ptr->len++] = c;
+}
+
+/* add null terminated string s to the end of sb at ptr. */
+
+void
+sb_add_string (ptr, s)
+     sb *ptr;
+     const char *s;
+{
+  int len = strlen (s);
+  sb_check (ptr, len);
+  memcpy (ptr->ptr + ptr->len, s, len);
+  ptr->len += len;
+}
+
+/* add string at s of length len to sb at ptr */
+
+void
+sb_add_buffer (ptr, s, len)
+     sb *ptr;
+     const char *s;
+     int len;
+{
+  sb_check (ptr, len);
+  memcpy (ptr->ptr + ptr->len, s, len);
+  ptr->len += len;
+}
+
+/* print the sb at ptr to the output file */
+
+void
+sb_print (outfile, ptr)
+     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;
+    }
+}
+
+void 
+sb_print_at (outfile, idx, ptr)
+     FILE *outfile;
+     int idx;
+     sb *ptr;
+{
+  int i;
+  for (i = idx; i < ptr->len; i++)
+    putc (ptr->ptr[i], outfile);
+}
+
+/* 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. */
+
+char *
+sb_name (in)
+     sb *in;
+{
+  /* stick a null on the end of the string */
+  sb_add_char (in, 0);
+  return in->ptr;
+}
+
+/* like sb_name, but don't include the null byte in the string.  */
+
+char *
+sb_terminate (in)
+     sb *in;
+{
+  sb_add_char (in, 0);
+  --in->len;
+  return in->ptr;
+}
+
+/* start at the index idx into the string in sb at ptr and skip
+   whitespace. return the index of the first non whitespace character */
+
+int
+sb_skip_white (idx, ptr)
+     int idx;
+     sb *ptr;
+{
+  while (idx < ptr->len
+        && (ptr->ptr[idx] == ' '
+            || ptr->ptr[idx] == '\t'))
+    idx++;
+  return idx;
+}
+
+/* start at the index idx into the sb at ptr. skips whitespace,
+   a comma and any following whitespace. returnes the index of the
+   next character. */
+
+int
+sb_skip_comma (idx, ptr)
+     int idx;
+     sb *ptr;
+{
+  while (idx < ptr->len
+        && (ptr->ptr[idx] == ' '
+            || ptr->ptr[idx] == '\t'))
+    idx++;
+
+  if (idx < ptr->len
+      && ptr->ptr[idx] == ',')
+    idx++;
+
+  while (idx < ptr->len
+        && (ptr->ptr[idx] == ' '
+            || ptr->ptr[idx] == '\t'))
+    idx++;
+
+  return idx;
+}