386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 17:53:15 +0000 (09:53 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 17:53:15 +0000 (09:53 -0800)
Work on file usr/src/usr.bin/gas/as.c
Work on file usr/src/usr.bin/gas/input-file.c
Work on file usr/src/usr.bin/gas/input-scrub.c
Work on file usr/src/usr.bin/gas/read.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/usr.bin/gas/as.c [new file with mode: 0644]
usr/src/usr.bin/gas/input-file.c [new file with mode: 0644]
usr/src/usr.bin/gas/input-scrub.c [new file with mode: 0644]
usr/src/usr.bin/gas/read.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gas/as.c b/usr/src/usr.bin/gas/as.c
new file mode 100644 (file)
index 0000000..db85525
--- /dev/null
@@ -0,0 +1,324 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)as.c       6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* as.c - GAS main program.
+   Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * Main program for AS; a 32-bit assembler of GNU.
+ * Understands command arguments.
+ * Has a few routines that don't fit in other modules because they
+ * are shared.
+ *
+ *
+ *                     bugs
+ *
+ * : initialisers
+ *     Since no-one else says they will support them in future: I
+ * don't support them now.
+ *
+ */
+
+#ifdef _POSIX_SOURCE
+#include <sys/types.h> /* For pid_t in signal.h */
+#endif
+#include <signal.h>
+
+#define COMMON
+#include "as.h"
+#include "struc-symbol.h"
+#include "write.h"
+               /* Warning!  This may have some slightly strange side effects
+                  if you try to compile two or more assemblers in the same
+                  directory!
+                */
+
+#ifndef SIGTY
+#define SIGTY int
+#endif
+
+SIGTY got_sig();
+
+#ifdef DONTDEF
+static char * gdb_symbol_file_name;
+long int gdb_begin();
+#endif
+
+char *myname;          /* argv[0] */
+extern char version_string[];
+\f
+main(argc,argv)
+int    argc;
+char   **argv;
+{
+       int     work_argc;      /* variable copy of argc */
+       char    **work_argv;    /* variable copy of argv */
+       char    *arg;           /* an arg to program */
+       char    a;              /* an arg flag (after -) */
+       static const int sig[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, 0};
+
+       extern int bad_error;   /* Did we hit a bad error ? */
+
+       char    *stralloc();    /* Make a (safe) copy of a string. */
+       void    symbol_begin();
+       void    read_begin();
+       void    write_object_file();
+
+       for(a=0;sig[a]!=0;a++)
+               if(signal(sig[a], SIG_IGN) != SIG_IGN)
+                       signal(sig[a], got_sig);
+
+       myname=argv[0];
+       bzero (flagseen, sizeof(flagseen)); /* aint seen nothing yet */
+       out_file_name   = "a.out";      /* default .o file */
+       symbol_begin();         /* symbols.c */
+       subsegs_begin();                /* subsegs.c */
+       read_begin();                   /* read.c */
+       md_begin();                     /* MACHINE.c */
+       input_scrub_begin();            /* input_scrub.c */
+#ifdef DONTDEF
+       gdb_symbol_file_name = 0;
+#endif
+       /*
+        * Parse arguments, but we are only interested in flags.
+        * When we find a flag, we process it then make it's argv[] NULL.
+        * This helps any future argv[] scanners avoid what we processed.
+        * Since it is easy to do here we interpret the special arg "-"
+        * to mean "use stdin" and we set that argv[] pointing to "".
+        * After we have munged argv[], the only things left are source file
+        * name(s) and ""(s) denoting stdin. These file names are used
+        * (perhaps more than once) later.
+        */
+       work_argc = argc-1;             /* don't count argv[0] */
+       work_argv = argv+1;             /* skip argv[0] */
+       for (;work_argc--;work_argv++) {
+               arg = * work_argv;      /* work_argv points to this argument */
+
+               if (*arg!='-')          /* Filename. We need it later. */
+                       continue;       /* Keep scanning args looking for flags. */
+               if (arg[1] == '-' && arg[2] == 0) {
+                       /* "--" as an argument means read STDIN */
+                       /* on this scan, we don't want to think about filenames */
+                       * work_argv = "";       /* Code that means 'use stdin'. */
+                       continue;
+               }
+                               /* This better be a switch. */
+               arg ++;         /* -> letter. */
+
+               while (a = * arg)  {/* scan all the 1-char flags */
+                       arg ++; /* arg -> after letter. */
+                       a &= 0x7F;      /* ascii only please */
+                       if (flagseen[a])
+                               as_warn("%s: Flag option -%c has already been seen!",myname,a);
+                       flagseen[a] = TRUE;
+                       switch (a) {
+                       case 'f':
+                               break;  /* -f means fast - no need for "app" preprocessor. */
+
+                       case 'D':
+                               /* DEBUG is implemented: it debugs different */
+                               /* things to other people's assemblers. */
+                               break;
+
+#ifdef DONTDEF
+                       case 'G':       /* GNU AS switch: include gdbsyms. */
+                               if (*arg)       /* Rest of argument is file-name. */
+                                       gdb_symbol_file_name = stralloc (arg);
+                               else if (work_argc) {   /* Next argument is file-name. */
+                                       work_argc --;
+                                       * work_argv = NULL; /* Not a source file-name. */
+                                       gdb_symbol_file_name = * ++ work_argv;
+                               } else
+                                       as_warn( "%s: I expected a filename after -G",myname);
+                               arg = "";       /* Finished with this arg. */
+                               break;
+#endif
+
+#ifndef WORKING_DOT_WORD
+                       case 'k':
+                               break;
+#endif
+
+                       case 'L': /* -L means keep L* symbols */
+                               break;
+
+                       case 'o':
+                               if (*arg)       /* Rest of argument is object file-name. */
+                                       out_file_name = stralloc (arg);
+                               else if (work_argc) {   /* Want next arg for a file-name. */
+                                       * work_argv = NULL; /* This is not a file-name. */
+                                       work_argc--;
+                                       out_file_name = * ++ work_argv;
+                               } else
+                                       as_warn("%s: I expected a filename after -o. \"%s\" assumed.",myname,out_file_name);
+                               arg = "";       /* Finished with this arg. */
+                               break;
+
+                       case 'R':
+                               /* -R means put data into text segment */
+                               break;
+
+                       case 'v':
+#ifdef VMS
+                               {
+                               extern char *compiler_version_string;
+                               compiler_version_string = arg;
+                               }
+#else /* not VMS */
+                               fprintf(stderr,version_string);
+                               if(*arg && strcmp(arg,"ersion"))
+                                       as_warn("Unknown -v option ignored");
+#endif
+                               while(*arg) arg++;      /* Skip the rest */
+                               break;
+
+                       case 'W':
+                               /* -W means don't warn about things */
+                               break;
+
+                       case 'g':
+                               /*
+                                * -g asks gas to produce gdb/dbx line number
+                                * and file name stabs so that an assembly
+                                * file can be handled by a source debugger.
+                                */
+                               break;
+
+                       default:
+                               --arg;
+                               if(md_parse_option(&arg,&work_argc,&work_argv)==0)
+                                       as_warn("%s: I don't understand '%c' flag!",myname,a);
+                               if(arg && *arg)
+                                       arg++;
+                               break;
+                       }
+               }
+               /*
+                * We have just processed a "-..." arg, which was not a
+                * file-name. Smash it so the
+                * things that look for filenames won't ever see it.
+                *
+                * Whatever work_argv points to, it has already been used
+                * as part of a flag, so DON'T re-use it as a filename.
+                */
+               *work_argv = NULL; /* NULL means 'not a file-name' */
+       }
+#ifdef DONTDEF
+       if (gdb_begin(gdb_symbol_file_name) == 0)
+               flagseen ['G'] = 0;     /* Don't do any gdbsym stuff. */
+#endif
+       /* Here with flags set up in flagseen[]. */
+       perform_an_assembly_pass(argc,argv); /* Assemble it. */
+       if (seen_at_least_1_file() && !bad_error)
+               write_object_file();/* relax() addresses then emit object file */
+       input_scrub_end();
+       md_end();                       /* MACHINE.c */
+#ifndef        VMS
+       exit(bad_error);                        /* WIN */
+#else  /* VMS */
+       exit(!bad_error);                       /* WIN */
+#endif /* VMS */
+}
+\f
+/*                     perform_an_assembly_pass()
+ *
+ * Here to attempt 1 pass over each input file.
+ * We scan argv[*] looking for filenames or exactly "" which is
+ * shorthand for stdin. Any argv that is NULL is not a file-name.
+ * We set need_pass_2 TRUE if, after this, we still have unresolved
+ * expressions of the form (unknown value)+-(unknown value).
+ *
+ * Note the un*x semantics: there is only 1 logical input file, but it
+ * may be a catenation of many 'physical' input files.
+ */
+perform_an_assembly_pass (argc, argv)
+int    argc;
+char **        argv;
+{
+       char *  buffer;         /* Where each bufferful of lines will start. */
+       void    read_a_source_file();
+       int saw_a_file = 0;
+
+       text_fix_root           = NULL;
+       data_fix_root           = NULL;
+       need_pass_2             = FALSE;
+
+       argv++;                 /* skip argv[0] */
+       argc--;                 /* skip argv[0] */
+       while (argc--) {
+               if (*argv) {            /* Is it a file-name argument? */
+                       /* argv -> "" if stdin desired, else -> filename */
+                       if (buffer = input_scrub_new_file (*argv) ) {
+                               saw_a_file++;
+                               read_a_source_file(buffer);
+                       }
+               }
+               argv++;                 /* completed that argv */
+       }
+       if(!saw_a_file)
+               if(buffer = input_scrub_new_file("") )
+                       read_a_source_file(buffer);
+}
+\f
+/*
+ *                     stralloc()
+ *
+ * Allocate memory for a new copy of a string. Copy the string.
+ * Return the address of the new string. Die if there is any error.
+ */
+
+char *
+stralloc (str)
+char * str;
+{
+       register char * retval;
+       register long int       len;
+
+       len = strlen (str) + 1;
+       retval = xmalloc (len);
+       (void)strcpy (retval, str);
+       return (retval);
+}
+\f
+lose()
+{
+       as_fatal( "%s: 2nd pass not implemented - get your code from random(3)",myname );
+}
+
+SIGTY
+got_sig(sig)
+int sig;
+{
+       static here_before = 0;
+
+       as_bad("Interrupted by signal %d",sig);
+       if(here_before++)
+               exit(1);
+}
+
+/* end: as.c */
diff --git a/usr/src/usr.bin/gas/input-file.c b/usr/src/usr.bin/gas/input-file.c
new file mode 100644 (file)
index 0000000..8de1dcf
--- /dev/null
@@ -0,0 +1,306 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input-file.c       6.2 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* input_file.c - Deal with Input Files -
+   Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * Confines all details of reading source bytes to this module.
+ * All O/S specific crocks should live here.
+ * What we lose in "efficiency" we gain in modularity.
+ * Note we don't need to #include the "as.h" file. No common coupling!
+ */
+
+#define NDEBUG         /* JF remove asserts */
+
+#ifdef USG
+#define index strchr
+/* JF:  What's the difference between _IOLBF and _IOFBF ? */
+#define setbuffer(stream, buf, size) setvbuf((stream), (buf), _IOFBF, (size))
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+/* #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h> */
+
+/* #include "style.h" */
+#include "input-file.h"
+
+/* This variable is non-zero if the file currently being read should be
+   preprocessed by app.  It is zero if the file can be read straight in.
+ */
+int preprocess = 0;
+
+void   as_perror();
+
+/*
+ * This code opens a file, then delivers BUFFER_SIZE character
+ * chunks of the file on demand.
+ * BUFFER_SIZE is supposed to be a number chosen for speed.
+ * The caller only asks once what BUFFER_SIZE is, and asks before
+ * the nature of the input files (if any) is known.
+ */
+
+#define BUFFER_SIZE (32 * 1024)
+
+static char in_buf[BUFFER_SIZE];
+
+/*
+ * We use static data: the data area is not sharable.
+ */
+
+FILE *f_in;    /* JF do things the RIGHT way */
+/* static JF remove static so app.c can use file_name */
+char * file_name;
+\f
+/* These hooks accomodate most operating systems. */
+
+void
+input_file_begin ()
+{
+  /* file_handle = -1; */
+  f_in = (FILE *)0;
+}
+
+void
+input_file_end ()
+{
+}
+
+int                            /* Return BUFFER_SIZE. */
+input_file_buffer_size ()
+{
+  return (BUFFER_SIZE);
+}
+
+int
+input_file_is_open ()
+{
+  /* return (file_handle >= 0); */
+  return f_in!=(FILE *)0;
+}
+\f
+#ifdef DONTDEF         /* JF save old version in case we need it */
+void
+input_file_open (filename, preprocess, debugging)
+     char *    filename;       /* "" means use stdin. Must not be 0. */
+     int       preprocess;     /* TRUE if needs app. */
+     int       debugging;      /* TRUE if we are debugging assembler. */
+{
+  assert( filename != 0 );     /* Filename may not be NULL. */
+  if (filename [0])
+    {                          /* We have a file name. Suck it and see. */
+      file_handle = open (filename, O_RDONLY, 0);
+      file_name = filename;
+    }
+  else
+    {                          /* use stdin for the input file. */
+      file_handle = fileno (stdin);
+      file_name = "{standard input}"; /* For error messages. */
+    }
+  if (file_handle < 0)
+      as_perror ("Can't open %s for reading", file_name);
+  if ( preprocess )
+    {
+/*
+ * This code was written in haste for a frobbed BSD 4.2.
+ * I have a flight to catch: will someone please do proper
+ * error checks? - Dean.
+ */
+      int      pid;
+      char temporary_file_name [12];
+      int      fd;
+      union wait       status;
+      char     *mktemp();
+
+      (void)strcpy (temporary_file_name, "#appXXXXXX");
+      (void)mktemp (temporary_file_name);
+      pid = vfork ();
+      if (pid == -1)
+       {
+         as_perror ("Vfork failed", file_name);
+         _exit (144);
+       }
+      if (pid == 0)
+       {
+         (void)dup2 (file_handle, fileno(stdin));
+         fd = open (temporary_file_name, O_WRONLY + O_TRUNC + O_CREAT, 0666);
+         if (fd == -1)
+           {
+             (void)write(2,"Can't open temporary\n",21);
+             _exit (99);
+           }
+         (void)dup2 (fd, fileno(stdout));
+/* JF for testing #define PREPROCESSOR "/lib/app" */
+#define PREPROCESSOR "./app"
+         execl (PREPROCESSOR, PREPROCESSOR, 0);
+         execl ("app","app",0);
+         (void)write(2,"Exec of app failed.  Get help.\n",31);
+         (void)unlink(temporary_file_name);
+         _exit (11);
+       }
+      (void)wait (& status);
+      if (status.w_status & 0xFF00)            /* JF was 0xF000, was wrong */
+       {
+         file_handle = -1;
+         as_warn( "Can't preprocess file \"%s\", status = %xx", file_name, status.w_status );
+       }
+      else
+       {
+         file_handle = open (temporary_file_name, O_RDONLY, 0);
+         if ( ! debugging && unlink(temporary_file_name))
+           as_perror ("Can't delete temp file %s", temporary_file_name);
+       }
+      if (file_handle == -1)
+         as_perror ("Can't retrieve temp file %s", temporary_file_name);
+    }
+}
+#else
+
+void
+input_file_open (filename,pre)
+     char *    filename;       /* "" means use stdin. Must not be 0. */
+     int pre;
+{
+       int     c;
+       char    buf[80];
+
+       preprocess = pre;
+
+       assert( filename != 0 );        /* Filename may not be NULL. */
+       if (filename [0]) {     /* We have a file name. Suck it and see. */
+               f_in=fopen(filename,"r");
+               file_name=filename;
+       } else {                        /* use stdin for the input file. */
+               f_in = stdin;
+               file_name = "{standard input}"; /* For error messages. */
+       }
+       if (f_in==(FILE *)0) {
+               as_perror ("Can't open %s for reading", file_name);
+               return;
+       }
+#ifndef VMS
+       setbuffer(f_in,in_buf,BUFFER_SIZE);
+#endif /* VMS */
+       c=getc(f_in);
+       if(c=='#') {    /* Begins with comment, may not want to preprocess */
+               c=getc(f_in);
+               if(c=='N') {
+                       fgets(buf,80,f_in);
+                       if(!strcmp(buf,"O_APP\n"))
+                               preprocess=0;
+                       if(!index(buf,'\n'))
+                               ungetc('#',f_in);       /* It was longer */
+                       else
+                               ungetc('\n',f_in);
+               } else if(c=='\n')
+                       ungetc('\n',f_in);
+               else
+                       ungetc('#',f_in);
+       } else
+               ungetc(c,f_in);
+
+#ifdef DONTDEF
+       if ( preprocess ) {
+               char temporary_file_name [17];
+               char    *mktemp();
+               FILE    *f_out;
+
+               (void)strcpy (temporary_file_name, "/tmp/#appXXXXXX");
+               (void)mktemp (temporary_file_name);
+               f_out=fopen(temporary_file_name,"w+");
+               if(f_out==(FILE *)0)
+                       as_perror("Can't open temp file %s",temporary_file_name);
+
+                       /* JF this will have to be moved on any system that
+                          does not support removal of open files.  */
+               (void)unlink(temporary_file_name);/* JF do it NOW */
+               do_scrub(f_in,f_out);
+               (void)fclose(f_in);     /* All done with it */
+               (void)rewind(f_out);
+               f_in=f_out;
+       }
+#endif
+}
+#endif
+
+char *
+input_file_give_next_buffer (where)
+     char *            where;  /* Where to place 1st character of new buffer. */
+{
+  char *       return_value;   /* -> Last char of what we read, + 1. */
+  register int size;
+
+  if (f_in == (FILE *)0)
+      return 0;
+      /*
+       * fflush (stdin); could be done here if you want to synchronise
+       * stdin and stdout, for the case where our input file is stdin.
+       * Since the assembler shouldn't do any output to stdout, we
+       * don't bother to synch output and input.
+       */
+  /* size = read (file_handle, where, BUFFER_SIZE); */
+  if(preprocess) {
+       char *p;
+       int n;
+       int ch;
+       extern FILE *scrub_file;
+       int scrub_from_file();
+       void scrub_to_file();
+       int do_scrub_next_char();
+
+       scrub_file=f_in;
+       for(p=where,n=BUFFER_SIZE;n;--n) {
+               ch=do_scrub_next_char(scrub_from_file,scrub_to_file);
+               if(ch==EOF)
+                       break;
+               *p++=ch;
+       }
+       size=BUFFER_SIZE-n;
+  } else
+       size= fread(where,sizeof(char),BUFFER_SIZE,f_in);
+  if (size < 0)
+    {
+      as_perror ("Can't read from %s", file_name);
+      size = 0;
+    }
+  if (size)
+    return_value = where + size;
+  else
+    {
+      if (fclose (f_in))
+       as_perror ("Can't close %s", file_name);
+      f_in = (FILE *)0;
+      return_value = 0;
+    }
+  return (return_value);
+}
+
+/* end: input_file.c */
diff --git a/usr/src/usr.bin/gas/input-scrub.c b/usr/src/usr.bin/gas/input-scrub.c
new file mode 100644 (file)
index 0000000..71af8a0
--- /dev/null
@@ -0,0 +1,427 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input-scrub.c      6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* input_scrub.c - layer between app and the rest of the world
+   Copyright (C) 1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "as.h"
+#include "read.h"
+#include "input-file.h"
+
+/*
+ * O/S independent module to supply buffers of sanitised source code
+ * to rest of assembler. We get raw input data of some length.
+ * Also looks after line numbers, for e.g. error messages.
+ * This module used to do the sanitising, but now a pre-processor program
+ * (app) does that job so this module is degenerate.
+ * Now input is pre-sanitised, so we only worry about finding the
+ * last partial line. A buffer of full lines is returned to caller.
+ * The last partial line begins the next buffer we build and return to caller.
+ * The buffer returned to caller is preceeded by BEFORE_STRING and followed
+ * by AFTER_STRING. The last character before AFTER_STRING is a newline.
+ */
+
+/*
+ * We expect the following sanitation has already been done.
+ *
+ * No comments, reduce a comment to a space.
+ * Reduce a tab to a space unless it is 1st char of line.
+ * All multiple tabs and spaces collapsed into 1 char. Tab only
+ *   legal if 1st char of line.
+ * # line file statements converted to .line x;.file y; statements.
+ * Escaped newlines at end of line: remove them but add as many newlines
+ *   to end of statement as you removed in the middle, to synch line numbers.
+ */
+\f
+#define BEFORE_STRING ("\n")
+#define AFTER_STRING ("\0")    /* bcopy of 0 chars might choke. */
+#define BEFORE_SIZE (1)
+#define AFTER_SIZE  (1)
+
+static char *  buffer_start;   /* -> 1st char of full buffer area. */
+static char *  partial_where;  /* -> after last full line in buffer. */
+static int     partial_size;   /* >=0. Number of chars in partial line in buffer. */
+static char    save_source [AFTER_SIZE];
+                               /* Because we need AFTER_STRING just after last */
+                               /* full line, it clobbers 1st part of partial */
+                               /* line. So we preserve 1st part of partial */
+                               /* line here. */
+static int     buffer_length;  /* What is the largest size buffer that */
+                               /* input_file_give_next_buffer() could */
+                               /* return to us? */
+
+static void as_1_char ();
+
+/*
+We never have more than one source file open at once.
+We may, however, read more than 1 source file in an assembly.
+NULL means we have no file open right now.
+*/
+
+
+/*
+We must track the physical file and line number for error messages.
+We also track a "logical" file and line number corresponding to (C?)
+compiler source line numbers.
+Whenever we open a file we must fill in physical_input_file. So if it is NULL
+we have not opened any files yet.
+*/
+
+static
+char *         physical_input_file,
+     *         logical_input_file;
+
+
+
+typedef unsigned int line_numberT;     /* 1-origin line number in a source file. */
+                               /* A line ends in '\n' or eof. */
+
+static
+line_numberT   physical_input_line,
+               logical_input_line;
+\f
+void
+input_scrub_begin ()
+{
+  know( strlen(BEFORE_STRING) == BEFORE_SIZE );
+  know( strlen( AFTER_STRING) ==  AFTER_SIZE );
+
+  input_file_begin ();
+
+  buffer_length = input_file_buffer_size ();
+
+  buffer_start = xmalloc ((long)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE));
+  bcopy (BEFORE_STRING, buffer_start, (int)BEFORE_SIZE);
+
+  /* Line number things. */
+  logical_input_line = 0;
+  logical_input_file = (char *)NULL;
+  physical_input_file = NULL;  /* No file read yet. */
+  do_scrub_begin();
+}
+
+void
+input_scrub_end ()
+{
+  input_file_end ();
+}
+
+char *                         /* Return start of caller's part of buffer. */
+input_scrub_new_file (filename)
+     char *    filename;
+{
+  input_file_open (filename, !flagseen['f']);
+  physical_input_file = filename[0] ? filename : "{standard input}";
+  physical_input_line = 0;
+
+  partial_size = 0;
+  return (buffer_start + BEFORE_SIZE);
+}
+
+char *
+input_scrub_next_buffer (bufp)
+char **bufp;
+{
+  register char *      limit;  /* -> just after last char of buffer. */
+
+#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)
+      bcopy(save_source, partial_where,(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;
+    bcopy(partial_where, save_source,(int)AFTER_SIZE);
+    bcopy(AFTER_STRING, partial_where, (int)AFTER_SIZE);
+
+    save_buffer = *bufp;
+    *bufp = out_string;
+
+    return partial_where;
+  }
+
+  /* We're not preprocessing.  Do the right thing */
+#endif
+  if (partial_size)
+    {
+      bcopy (partial_where, buffer_start + BEFORE_SIZE, (int)partial_size);
+      bcopy (save_source, buffer_start + BEFORE_SIZE, (int)AFTER_SIZE);
+    }
+  limit = input_file_give_next_buffer (buffer_start + BEFORE_SIZE + partial_size);
+  if (limit)
+    {
+      register char *  p;      /* Find last newline. */
+
+      for (p = limit;   * -- p != '\n';   )
+       {
+       }
+      ++ p;
+      if (p <= buffer_start + BEFORE_SIZE)
+       {
+         as_fatal ("Source line too long. Please change file %s then rebuild assembler.", __FILE__);
+       }
+      partial_where = p;
+      partial_size = limit - p;
+      bcopy (partial_where, save_source,  (int)AFTER_SIZE);
+      bcopy (AFTER_STRING, partial_where, (int)AFTER_SIZE);
+    }
+  else
+    {
+      partial_where = 0;
+      if (partial_size > 0)
+       {
+         as_warn( "Partial line at end of file ignored" );
+       }
+    }
+  return (partial_where);
+}
+\f
+/*
+ * The remaining part of this file deals with line numbers, error
+ * messages and so on.
+ */
+
+
+int
+seen_at_least_1_file ()                /* TRUE if we opened any file. */
+{
+  return (physical_input_file != NULL);
+}
+
+void
+bump_line_counters ()
+{
+  ++ physical_input_line;
+  ++ logical_input_line;
+}
+\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 fname is NULL, we don't change the current logical file name.
+ */
+void
+new_logical_line (fname, line_number)
+     char *    fname;          /* DON'T destroy it! We point to it! */
+     int       line_number;
+{
+  if ( fname )
+    {
+      logical_input_file = fname;
+    }
+  if ( line_number >= 0 )
+    {
+      logical_input_line = line_number;
+    }
+}
+\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.
+ */
+void
+as_where()
+{
+  char *p;
+  line_numberT line;
+
+  if (physical_input_file)
+    {                          /* we tried to read SOME source */
+      if (input_file_is_open())
+       {                       /* we can still read lines from source */
+#ifdef DONTDEF
+         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
+               p = logical_input_file ? logical_input_file : physical_input_file;
+               line = logical_input_line ? logical_input_line : physical_input_line;
+               fprintf(stderr,"%s:%u:", p, line);
+#endif
+       }
+      else
+       {
+#ifdef DONTDEF
+         fprintf (stderr," After reading source.\n");
+#else
+       p = logical_input_file ? logical_input_file : physical_input_file;
+       line = logical_input_line ? logical_input_line : physical_input_line;
+       fprintf (stderr,"%s:unknown:", p);
+#endif
+       }
+    }
+  else
+    {
+#ifdef DONTDEF
+      fprintf (stderr," Before reading source.\n");
+#else
+#endif
+    }
+}
+\f
+/*
+ * Support for source file debugging.  These functions handle
+ * logical lines and logical files.
+ */
+static char *saved_file;
+static int saved_len;
+static line_numberT saved_line;
+
+void
+filestab()
+{
+  char *file;
+  int len;
+
+  if (!physical_input_file ||
+      !input_file_is_open())
+    return;
+
+  file = logical_input_file ? logical_input_file : physical_input_file;
+
+  if (saved_file == 0 || strcmp(file, saved_file) != 0)
+    {
+      stabs(file);
+      len = strlen(file) + 1;
+      if (len > saved_len)
+       {
+         if (saved_file == 0)
+           saved_file = xmalloc(len);
+         else
+           saved_file = xrealloc(saved_file, len);
+         memcpy(saved_file, file, len);
+         saved_len = len;
+       }
+      else
+       strcpy(saved_file, file);
+      saved_line = 0;
+    }
+}
+
+void
+funcstab(func)
+     char *func;
+{
+  if (now_seg != SEG_TEXT)
+    return;
+
+  filestab();
+  stabf(func);
+}
+
+void
+linestab()
+{
+  line_numberT line;
+
+  if (now_seg != SEG_TEXT)
+    return;
+
+  filestab();
+
+  line = logical_input_line ? logical_input_line : physical_input_line;
+
+  if (saved_line == 0 || line != saved_line)
+    {
+      stabd(line);
+      saved_line = line;
+    }
+}
+\f
+/*
+ *                     a s _ h o w m u c h ( )
+ *
+ * Output to given stream how much of line we have scanned so far.
+ * Assumes we have scanned up to and including input_line_pointer.
+ * No free '\n' at end of line.
+ */
+void
+as_howmuch (stream)
+     FILE * stream;            /* Opened for write please. */
+{
+  register     char *  p;      /* Scan input line. */
+  /* register  char    c; JF unused */
+
+  for (p = input_line_pointer - 1;   * p != '\n';   --p)
+    {
+    }
+  ++ p;                                /* p -> 1st char of line. */
+  for (;  p <= input_line_pointer;  p++)
+    {
+      /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */
+      /* c = *p & 0xFF; JF unused */
+      as_1_char (*p, stream);
+    }
+}
+
+static void
+as_1_char (c,stream)
+     unsigned char c;
+     FILE *    stream;
+{
+  if ( c > 127 )
+    {
+      (void)putc( '%', stream);
+      c -= 128;
+    }
+  if ( c < 32 )
+    {
+      (void)putc( '^', stream);
+      c += '@';
+    }
+  (void)putc( c, stream);
+}
+
+/* end: input_scrub.c */
diff --git a/usr/src/usr.bin/gas/read.c b/usr/src/usr.bin/gas/read.c
new file mode 100644 (file)
index 0000000..8357107
--- /dev/null
@@ -0,0 +1,2188 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)read.c     6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* read.c - read a source file -
+   Copyright (C) 1986,1987 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#define MASK_CHAR (0xFF)       /* If your chars aren't 8 bits, you will
+                                  change this a bit.  But then, GNU isn't
+                                  spozed to run on your machine anyway.
+                                  (RMS is so shortsighted sometimes.)
+                                */
+
+#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16)
+                               /* This is the largest known floating point */
+                               /* format (for now). It will grow when we */
+                               /* do 4361 style flonums. */
+
+
+/* Routines that read assembler source text to build spagetti in memory. */
+/* Another group of these functions is in the as-expr.c module */
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "as.h"
+#include "read.h"
+#include "md.h"
+#include "hash.h"
+#include "obstack.h"
+#include "frags.h"
+#include "flonum.h"
+#include "struc-symbol.h"
+#include "expr.h"
+#include "symbols.h"
+
+#ifdef SPARC
+#include "sparc.h"
+#define OTHER_ALIGN
+#endif
+#ifdef I860
+#include "i860.h"
+#endif
+
+char * input_line_pointer;     /* -> next char of source file to parse. */
+
+
+#if BITS_PER_CHAR != 8
+The following table is indexed by [ (char) ] and will break if
+a char does not have exactly 256 states (hopefully 0:255!) !
+#endif
+
+const char                             /* used by is_... macros. our ctype[] */
+lex_type [256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,       /* @ABCDEFGHIJKLMNO */
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,       /* PQRSTUVWXYZ[\]^_ */
+  0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0,       /* _!"#$%&'()*+,-./ */
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,       /* 0123456789:;<=>? */
+  0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,       /* @ABCDEFGHIJKLMNO */
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3,       /* PQRSTUVWXYZ[\]^_ */
+  0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,       /* `abcdefghijklmno */
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0,       /* pqrstuvwxyz{|}~. */
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
+};
+
+
+/*
+ * In: a character.
+ * Out: TRUE if this character ends a line.
+ */
+#define _ (0)
+const char is_end_of_line [256] = {
+ _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, _, /* @abcdefghijklmno */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, /* 0123456789:;<=>? */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /*                  */
+ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _  /*                  */
+};
+#undef _
+
+                               /* Functions private to this file. */
+void                   equals();
+void                   big_cons();
+void                   cons();
+static char*           demand_copy_C_string();
+static char*           demand_copy_string();
+void                   demand_empty_rest_of_line();
+void                   float_cons();
+long int               get_absolute_expression();
+static char            get_absolute_expression_and_terminator();
+static segT            get_known_segmented_expression();
+void                   ignore_rest_of_line();
+static int             is_it_end_of_statement();
+static void            pobegin();
+static void            pseudo_set();
+static void            stab();
+static void            stringer();
+
+extern char line_comment_chars[];
+
+static char *  buffer_limit;   /* -> 1 + last char in buffer. */
+
+static char *  bignum_low;     /* Lowest char of bignum. */
+static char *  bignum_limit;   /* 1st illegal address of bignum. */
+static char *  bignum_high;    /* Highest char of bignum. */
+                               /* May point to (bignum_start-1). */
+                               /* Never >= bignum_limit. */
+static char *old_buffer = 0;   /* JF a hack */
+static char *old_input;
+static char *old_limit;
+
+#ifndef WORKING_DOT_WORD
+struct broken_word *broken_words;
+int new_broken_words = 0;
+#endif
+
+static void grow_bignum ();
+static int next_char_of_string ();
+\f
+void
+read_begin()
+{
+  pobegin();
+  obstack_begin( &notes, 5000 );
+#define BIGNUM_BEGIN_SIZE (16)
+  bignum_low = xmalloc((long)BIGNUM_BEGIN_SIZE);
+  bignum_limit = bignum_low + BIGNUM_BEGIN_SIZE;
+}
+\f
+/* set up pseudo-op tables */
+
+static struct hash_control *
+po_hash = NULL;                        /* use before set up: NULL-> address error */
+
+
+void   s_abort(),      s_align(),      s_comm(),       s_data();
+void   s_desc(),       s_even(),       s_file(),       s_fill();
+void   s_globl(),      s_lcomm(),      s_line(),       s_lsym();
+void   s_org(),        s_set(),        s_space(),      s_text();
+#ifdef VMS
+char const_flag = 0;
+void s_const();
+#endif
+
+#ifdef DONTDEF
+void   s_gdbline(),    s_gdblinetab();
+void   s_gdbbeg(),     s_gdbblock(),   s_gdbend(),     s_gdbsym();
+#endif
+
+void   stringer();
+void   cons();
+void   float_cons();
+void   big_cons();
+void   stab();
+
+static const pseudo_typeS
+potable[] =
+{
+  { "abort",   s_abort,        0       },
+  { "align",   s_align,        0       },
+  { "ascii",   stringer,       0       },
+  { "asciz",   stringer,       1       },
+  { "byte",    cons,           1       },
+  { "comm",    s_comm,         0       },
+#ifdef VMS
+  { "const",   s_const,        0       },
+#endif
+  { "data",    s_data,         0       },
+  { "desc",    s_desc,         0       },
+  { "double",  float_cons,     'd'     },
+  { "file",    s_file,         0       },
+  { "fill",    s_fill,         0       },
+  { "float",   float_cons,     'f'     },
+#ifdef DONTDEF
+  { "gdbbeg",  s_gdbbeg,       0       },
+  { "gdbblock",        s_gdbblock,     0       },
+  { "gdbend",  s_gdbend,       0       },
+  { "gdbsym",  s_gdbsym,       0       },
+  { "gdbline", s_gdbline,      0       },
+  { "gdblinetab",s_gdblinetab, 0       },
+#endif
+  { "globl",   s_globl,        0       },
+  { "int",     cons,           4       },
+  { "lcomm",   s_lcomm,        0       },
+  { "line",    s_line,         0       },
+  { "long",    cons,           4       },
+  { "lsym",    s_lsym,         0       },
+  { "octa",    big_cons,       16      },
+  { "org",     s_org,          0       },
+  { "quad",    big_cons,       8       },
+  { "set",     s_set,          0       },
+  { "short",   cons,           2       },
+  { "single",  float_cons,     'f'     },
+  { "space",   s_space,        0       },
+  { "stabd",   stab,           'd'     },
+  { "stabn",   stab,           'n'     },
+  { "stabs",   stab,           's'     },
+  { "text",    s_text,         0       },
+#ifndef SPARC
+  { "word",    cons,           2       },
+#endif
+  { NULL}      /* end sentinel */
+};
+
+static void
+pobegin()
+{
+  char *       errtxt;         /* error text */
+  const pseudo_typeS * pop;
+
+  po_hash = hash_new();
+  errtxt = "";                 /* OK so far */
+  for (pop=potable; pop->poc_name && !*errtxt; pop++)
+    {
+      errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop);
+    }
+
+  for(pop=md_pseudo_table; pop->poc_name && !*errtxt; pop++)
+      errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop);
+
+  if (*errtxt)
+    {
+      as_fatal ("error constructing pseudo-op table");
+    }
+}                              /* pobegin() */
+\f
+/*                     read_a_source_file()
+ *
+ * File has already been opened, and will be closed by our caller.
+ *
+ * We read the file, putting things into a web that
+ * represents what we have been reading.
+ */
+void
+read_a_source_file (buffer)
+     char *    buffer;         /* 1st character of each buffer of lines is here. */
+{
+  register char                c;
+  register char *      s;      /* string of symbol, '\0' appended */
+  register int         temp;
+  /* register struct frag * fragP; JF unused */        /* a frag we just made */
+  pseudo_typeS *pop;
+#ifdef DONTDEF
+  void gdb_block_beg();
+  void gdb_block_position();
+  void gdb_block_end();
+  void gdb_symbols_fixup();
+#endif
+
+  subseg_new (SEG_TEXT, 0);
+  while ( buffer_limit = input_scrub_next_buffer (&buffer) )
+    {                          /* We have another line to parse. */
+      know( buffer_limit [-1] == '\n' ); /* Must have a sentinel. */
+      input_line_pointer = buffer;
+ contin:       /* JF this goto is my fault I admit it.  Someone brave please re-write
+                  the whole input section here?  Pleeze??? */
+      while ( input_line_pointer < buffer_limit )
+       {                       /* We have more of this buffer to parse. */
+         /*
+          * We now have input_line_pointer -> 1st char of next line.
+          * If input_line_pointer [-1] == '\n' then we just
+          * scanned another line: so bump line counters.
+          */
+         if (input_line_pointer [-1] == '\n')
+           {
+             bump_line_counters ();
+           }
+         /*
+          * We are at the begining of a line, or similar place.
+          * We expect a well-formed assembler statement.
+          * A "symbol-name:" is a statement.
+          *
+          * Depending on what compiler is used, the order of these tests
+          * may vary to catch most common case 1st.
+          * Each test is independent of all other tests at the (top) level.
+          * PLEASE make a compiler that doesn't use this assembler.
+          * It is crufty to waste a compiler's time encoding things for this
+          * assembler, which then wastes more time decoding it.
+          * (And communicating via (linear) files is silly!
+          * If you must pass stuff, please pass a tree!)
+          */
+         if ( (c= * input_line_pointer ++) == '\t' || c == ' ' || c=='\f')
+           {
+             c = * input_line_pointer ++;
+           }
+         know( c != ' ' );     /* No further leading whitespace. */
+         /*
+          * C is the 1st significant character.
+          * Input_line_pointer points after that character.
+          */
+         if ( is_name_beginner(c) )
+           {                   /* want user-defined label or pseudo/opcode */
+             s = -- input_line_pointer;
+             c = get_symbol_end(); /* name's delimiter */
+             /*
+              * C is character after symbol.
+              * That character's place in the input line is now '\0'.
+              * S points to the beginning of the symbol.
+              *   [In case of pseudo-op, s -> '.'.]
+              * Input_line_pointer -> '\0' where c was.
+              */
+             if ( c == ':' )
+               {
+                 if (flagseen['g'])
+                   /* set line number for function definition */
+                   funcstab(s);
+                 colon(s);     /* user-defined label */
+                 * input_line_pointer ++ = ':'; /* Put ':' back for error messages' sake. */
+                               /* Input_line_pointer -> after ':'. */
+                 SKIP_WHITESPACE();
+               }
+             else if(c=='=' || input_line_pointer[1]=='=')             /* JF deal with FOO=BAR */
+               {
+                 equals(s);
+                 demand_empty_rest_of_line();
+               }
+             else
+               {               /* expect pseudo-op or machine instruction */
+                 if ( *s=='.' )
+                   {
+                     /*
+                      * PSEUDO - OP.
+                      *
+                      * WARNING: c has next char, which may be end-of-line.
+                      * We lookup the pseudo-op table with s+1 because we
+                      * already know that the pseudo-op begins with a '.'.
+                      */
+
+                     pop= (pseudo_typeS *) hash_find (po_hash, s+1);
+
+                     /* Print the error msg now, while we still can */
+                     if(!pop)
+                         as_bad("Unknown pseudo-op:  '%s'",s);
+
+                               /* Put it back for error messages etc. */
+                     * input_line_pointer = c;
+                               /* The following skip of whitespace is compulsory. */
+                               /* A well shaped space is sometimes all that seperates keyword from operands. */
+                     if ( c == ' ' || c == '\t' )
+                       {       /* Skip seperator after keyword. */
+                         input_line_pointer ++;
+                       }
+                     /*
+                      * Input_line is restored.
+                      * Input_line_pointer -> 1st non-blank char
+                      * after pseudo-operation.
+                      */
+                       if(!pop) {
+                         ignore_rest_of_line();
+                         break;
+                       }
+                       else
+                         (*pop->poc_handler)(pop->poc_val);
+                   }
+                 else
+                   {           /* machine instruction */
+                     /* If source file debugging, emit a stab. */
+                     if (flagseen['g'])
+                       linestab();
+
+                     /* WARNING: c has char, which may be end-of-line. */
+                     /* Also: input_line_pointer -> `\0` where c was. */
+                     * input_line_pointer = c;
+                     while ( ! is_end_of_line [* input_line_pointer] )
+                       {
+                         input_line_pointer ++;
+                       }
+                     c = * input_line_pointer;
+                     * input_line_pointer = '\0';
+                     md_assemble (s);  /* Assemble 1 instruction. */
+                     * input_line_pointer ++ = c;
+                     /* We resume loop AFTER the end-of-line from this instruction */
+                   }           /* if (*s=='.') */
+               }               /* if c==':' */
+             continue;
+           }                   /* if (is_name_beginner(c) */
+
+
+         if ( is_end_of_line [c] )
+           {                   /* empty statement */
+             continue;
+           }
+
+         if ( isdigit(c) )
+           {                   /* local label  ("4:") */
+             temp = c - '0';
+#ifdef SUN_ASM_SYNTAX
+             if( *input_line_pointer=='$')
+               input_line_pointer++;
+#endif
+             if ( * input_line_pointer ++ == ':' )
+               {
+                 local_colon (temp);
+               }
+             else
+               {
+                 as_bad( "Spurious digit %d.", temp);
+                 input_line_pointer -- ;
+                 ignore_rest_of_line();
+               }
+             continue;
+           }
+         if(c && index(line_comment_chars,c)) {        /* Its a comment.  Better say APP or NO_APP */
+               char *ends;
+               char *strstr();
+               char    *new_buf;
+               char    *new_tmp;
+               int     new_length;
+               char    *tmp_buf = 0;
+               extern char *scrub_string,*scrub_last_string;
+               int     scrub_from_string();
+               void    scrub_to_string();
+
+               bump_line_counters();
+               s=input_line_pointer;
+               if(strncmp(s,"APP\n",4))
+                       continue;       /* We ignore it */
+               s+=4;
+
+               ends=strstr(s,"#NO_APP\n");
+
+               if(!ends) {
+                       int     tmp_len;
+                       int     num;
+
+                       /* The end of the #APP wasn't in this buffer.  We
+                          keep reading in buffers until we find the #NO_APP
+                          that goes with this #APP  There is one.  The specs
+                          guarentee it. . .*/
+                       tmp_len=buffer_limit-s;
+                       tmp_buf=xmalloc(tmp_len);
+                       bcopy(s,tmp_buf,tmp_len);
+                       do {
+                               new_tmp = input_scrub_next_buffer(&buffer);
+                               if(!new_tmp)
+                                       break;
+                               else
+                                       buffer_limit = new_tmp;
+                               input_line_pointer = buffer;
+                               ends = strstr(buffer,"#NO_APP\n");
+                               if(ends)
+                                       num=ends-buffer;
+                               else
+                                       num=buffer_limit-buffer;
+
+                               tmp_buf=xrealloc(tmp_buf,tmp_len+num);
+                               bcopy(buffer,tmp_buf+tmp_len,num);
+                               tmp_len+=num;
+                       } while(!ends);
+
+                       input_line_pointer= ends ? ends+8 : NULL;
+
+                       s=tmp_buf;
+                       ends=s+tmp_len;
+
+               } else {
+                       input_line_pointer=ends+8;
+               }
+               new_buf=xmalloc(100);
+               new_length=100;
+               new_tmp=new_buf;
+
+               scrub_string=s;
+               scrub_last_string = ends;
+               for(;;) {
+                       int ch;
+
+                       ch=do_scrub_next_char(scrub_from_string,scrub_to_string);
+                       if(ch==EOF) break;
+                       *new_tmp++=ch;
+                       if(new_tmp==new_buf+new_length) {
+                               new_buf=xrealloc(new_buf,new_length+100);
+                               new_tmp=new_buf+new_length;
+                               new_length+=100;
+                       }
+               }
+
+               if(tmp_buf)
+                       free(tmp_buf);
+               old_buffer=buffer;
+               old_input=input_line_pointer;
+               old_limit=buffer_limit;
+               buffer=new_buf;
+               input_line_pointer=new_buf;
+               buffer_limit=new_tmp;
+               continue;
+         }
+
+         as_bad("Junk character %d.",c);
+         ignore_rest_of_line();
+       }                       /* while (input_line_pointer<buffer_limit )*/
+       if(old_buffer) {
+               bump_line_counters();
+               if(old_input == 0)
+                       return;
+               buffer=old_buffer;
+               input_line_pointer=old_input;
+               buffer_limit=old_limit;
+               old_buffer = 0;
+               goto contin;
+       }
+    }                          /* while (more bufrers to scan) */
+}                              /* read_a_source_file() */
+
+void
+s_abort()
+{
+       as_fatal(".abort detected.  Abandoning ship.");
+}
+
+#ifdef OTHER_ALIGN
+static void
+s_align()
+{
+    register unsigned int temp;
+    register long int temp_fill;
+    unsigned int i;
+
+    temp = get_absolute_expression ();
+#define MAX_ALIGNMENT (1 << 15)
+    if ( temp > MAX_ALIGNMENT ) {
+       as_bad("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT);
+    }
+
+    /*
+     * For the sparc, `.align (1<<n)' actually means `.align n'
+     * so we have to convert it.
+     */
+    if (temp != 0) {
+       for (i = 0; (temp & 1) == 0; temp >>= 1, ++i)
+           ;
+    }
+    if (temp != 1)
+       as_bad("Alignment not a power of 2");
+
+    temp = i;
+    if (*input_line_pointer == ',') {
+       input_line_pointer ++;
+       temp_fill = get_absolute_expression ();
+    } else {
+       temp_fill = 0;
+    }
+    /* Only make a frag if we HAVE to. . . */
+    if (temp && ! need_pass_2)
+       frag_align (temp, (int)temp_fill);
+
+    demand_empty_rest_of_line();
+}
+#else
+
+void
+s_align()
+{
+       register int temp;
+       register long int temp_fill;
+
+       temp = get_absolute_expression ();
+#define MAX_ALIGNMENT (15)
+       if ( temp > MAX_ALIGNMENT )
+               as_bad("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT);
+       else if ( temp < 0 ) {
+               as_bad("Alignment negative. 0 assumed.");
+               temp = 0;
+       }
+       if ( *input_line_pointer == ',' ) {
+               input_line_pointer ++;
+               temp_fill = get_absolute_expression ();
+       } else
+               temp_fill = 0;
+       /* Only make a frag if we HAVE to. . . */
+       if ( temp && ! need_pass_2 )
+               frag_align (temp, (int)temp_fill);
+       demand_empty_rest_of_line();
+}
+#endif
+
+void
+s_comm()
+{
+       register char *name;
+       register char c;
+       register char *p;
+       register int temp;
+       register symbolS *      symbolP;
+
+       name = input_line_pointer;
+       c = get_symbol_end();
+       /* just after name is now '\0' */
+       p = input_line_pointer;
+       *p = c;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               as_bad("Expected comma after symbol-name");
+               ignore_rest_of_line();
+               return;
+       }
+       input_line_pointer ++; /* skip ',' */
+       if ( (temp = get_absolute_expression ()) < 0 ) {
+               as_warn(".COMMon length (%d.) <0! Ignored.", temp);
+               ignore_rest_of_line();
+               return;
+       }
+       *p = 0;
+       symbolP = symbol_find_or_make (name);
+       *p = c;
+       if (   (symbolP -> sy_type & N_TYPE) != N_UNDF ||
+ symbolP -> sy_other != 0 || symbolP -> sy_desc != 0) {
+               as_warn( "Ignoring attempt to re-define symbol");
+               ignore_rest_of_line();
+               return;
+       }
+       if (symbolP -> sy_value) {
+               if (symbolP -> sy_value != temp)
+                       as_warn( "Length of .comm \"%s\" is already %d. Not changed to %d.",
+ symbolP -> sy_name, symbolP -> sy_value, temp);
+       } else {
+               symbolP -> sy_value = temp;
+               symbolP -> sy_type |= N_EXT;
+       }
+#ifdef VMS
+       if(!temp)
+               symbolP->sy_other = const_flag;
+#endif
+       know( symbolP -> sy_frag == &zero_address_frag );
+       demand_empty_rest_of_line();
+}
+
+#ifdef VMS
+void
+s_const()
+{
+       register int temp;
+
+       temp = get_absolute_expression ();
+       subseg_new (SEG_DATA, (subsegT)temp);
+       const_flag = 1;
+       demand_empty_rest_of_line();
+}
+#endif
+
+void
+s_data()
+{
+       register int temp;
+
+       temp = get_absolute_expression ();
+       subseg_new (SEG_DATA, (subsegT)temp);
+#ifdef VMS
+       const_flag = 0;
+#endif
+       demand_empty_rest_of_line();
+}
+
+void
+s_desc()
+{
+       register char *name;
+       register char c;
+       register char *p;
+       register symbolS *      symbolP;
+       register int temp;
+
+       /*
+        * Frob invented at RMS' request. Set the n_desc of a symbol.
+        */
+       name = input_line_pointer;
+       c = get_symbol_end();
+       p = input_line_pointer;
+       symbolP = symbol_table_lookup (name);
+       * p = c;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               *p = 0;
+               as_bad("Expected comma after name \"%s\"", name);
+               *p = c;
+               ignore_rest_of_line();
+       } else {
+               input_line_pointer ++;
+               temp = get_absolute_expression ();
+               *p = 0;
+               symbolP = symbol_find_or_make (name);
+               *p = c;
+               symbolP -> sy_desc = temp;
+       }
+       demand_empty_rest_of_line();
+}
+
+void
+s_file()
+{
+       register char *s;
+       int     length;
+
+       /* Some assemblers tolerate immediately following '"' */
+       if ( s = demand_copy_string( & length ) ) {
+               new_logical_line (s, -1);
+               demand_empty_rest_of_line();
+       }
+}
+
+void
+s_fill()
+{
+       long int temp_repeat;
+       long int temp_size;
+       register long int temp_fill;
+       char    *p;
+
+       if ( get_absolute_expression_and_terminator(& temp_repeat) != ',' ) {
+               input_line_pointer --; /* Backup over what was not a ','. */
+               as_warn("Expect comma after rep-size in .fill");
+               ignore_rest_of_line();
+               return;
+       }
+       if ( get_absolute_expression_and_terminator( & temp_size) != ',' ) {
+                 input_line_pointer --; /* Backup over what was not a ','. */
+                 as_warn("Expected comma after size in .fill");
+                 ignore_rest_of_line();
+                 return;
+       }
+       /*
+        * This is to be compatible with BSD 4.2 AS, not for any rational reason.
+        */
+#define BSD_FILL_SIZE_CROCK_8 (8)
+       if ( temp_size > BSD_FILL_SIZE_CROCK_8 ) {
+               as_bad(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8);
+               temp_size = BSD_FILL_SIZE_CROCK_8 ;
+       } if ( temp_size < 0 ) {
+               as_warn("Size negative: .fill ignored.");
+               temp_size = 0;
+       } else if ( temp_repeat <= 0 ) {
+               as_warn("Repeat < 0, .fill ignored");
+               temp_size = 0;
+       }
+       temp_fill = get_absolute_expression ();
+       if ( temp_size && !need_pass_2 ) {
+               p = frag_var (rs_fill, (int)temp_size, (int)temp_size, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0);
+               bzero (p, (int)temp_size);
+/*
+ * The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX flavoured AS.
+ * The following bizzare behaviour is to be compatible with above.
+ * I guess they tried to take up to 8 bytes from a 4-byte expression
+ * and they forgot to sign extend. Un*x Sux.
+ */
+#define BSD_FILL_SIZE_CROCK_4 (4)
+               md_number_to_chars (p, temp_fill, temp_size > BSD_FILL_SIZE_CROCK_4 ? BSD_FILL_SIZE_CROCK_4 : (int)temp_size);
+/*
+ * Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes)
+ * but emits no error message because it seems a legal thing to do.
+ * It is a degenerate case of .fill but could be emitted by a compiler.
+ */
+       }
+       demand_empty_rest_of_line();
+}
+
+#ifdef DONTDEF
+void
+s_gdbbeg()
+{
+       register int temp;
+
+       temp = get_absolute_expression ();
+       if (temp < 0)
+               as_warn( "Block number <0. Ignored." );
+       else if (flagseen ['G'])
+               gdb_block_beg ( (long int) temp, frag_now, (long int)(obstack_next_free(& frags) - frag_now -> fr_literal));
+       demand_empty_rest_of_line ();
+}
+
+void
+s_gdbblock()
+{
+       register int    position;
+       int     temp;
+
+       if (get_absolute_expression_and_terminator (&temp) != ',') {
+               as_warn( "expected comma before position in .gdbblock");
+               --input_line_pointer;
+               ignore_rest_of_line ();
+               return;
+       }
+       position = get_absolute_expression ();
+       if (flagseen ['G'])
+               gdb_block_position ((long int) temp, (long int) position);
+       demand_empty_rest_of_line ();
+}
+
+void
+s_gdbend()
+{
+       register int temp;
+
+       temp = get_absolute_expression ();
+       if (temp < 0)
+               as_warn( "Block number <0. Ignored." );
+       else if (flagseen ['G'])
+               gdb_block_end ( (long int) temp, frag_now, (long int)(obstack_next_free(& frags) - frag_now -> fr_literal));
+       demand_empty_rest_of_line ();
+}
+
+void
+s_gdbsym()
+{
+       register char *name,
+                       *p;
+       register char c;
+       register symbolS *      symbolP;
+       register int temp;
+
+       name = input_line_pointer;
+       c = get_symbol_end();
+       p = input_line_pointer;
+       symbolP = symbol_find_or_make (name);
+       *p = c;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               as_warn("Expected comma after name");
+               ignore_rest_of_line();
+               return;
+       }
+       input_line_pointer ++;
+       if ( (temp = get_absolute_expression ()) < 0 ) {
+               as_warn("Bad GDB symbol file offset (%d.) <0! Ignored.", temp);
+               ignore_rest_of_line();
+               return;
+       }
+       if (flagseen ['G'])
+               gdb_symbols_fixup (symbolP, (long int)temp);
+       demand_empty_rest_of_line ();
+}
+
+void
+s_gdbline()
+{
+       int     file_number,
+               lineno;
+
+       if(get_absolute_expression_and_terminator(&file_number) != ',') {
+               as_warn("expected comman after filenum in .gdbline");
+               ignore_rest_of_line();
+               return;
+       }
+       lineno=get_absolute_expression();
+       if(flagseen['G'])
+               gdb_line(file_number,lineno);
+       demand_empty_rest_of_line();
+}
+
+
+void
+s_gdblinetab()
+{
+       int     file_number,
+               offset;
+
+       if(get_absolute_expression_and_terminator(&file_number) != ',') {
+               as_warn("expected comman after filenum in .gdblinetab");
+               ignore_rest_of_line();
+               return;
+       }
+       offset=get_absolute_expression();
+       if(flagseen['G'])
+               gdb_line_tab(file_number,offset);
+       demand_empty_rest_of_line();
+}
+#endif 
+
+void
+s_globl()
+{
+       register char *name;
+       register int c;
+       register symbolS *      symbolP;
+
+       do {
+               name = input_line_pointer;
+               c = get_symbol_end();
+               symbolP = symbol_find_or_make (name);
+               * input_line_pointer = c;
+               SKIP_WHITESPACE();
+               symbolP -> sy_type |= N_EXT;
+               if(c==',') {
+                       input_line_pointer++;
+                       SKIP_WHITESPACE();
+                       if(*input_line_pointer=='\n')
+                               c='\n';
+               }
+       } while(c==',');
+       demand_empty_rest_of_line();
+}
+
+void
+s_lcomm()
+{
+       register char *name;
+       register char c;
+       register char *p;
+       register int temp;
+       register symbolS *      symbolP;
+
+       name = input_line_pointer;
+       c = get_symbol_end();
+       p = input_line_pointer;
+       *p = c;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               as_warn("Expected comma after name");
+               ignore_rest_of_line();
+               return;
+       }
+       input_line_pointer ++;
+       if ( (temp = get_absolute_expression ()) < 0 ) {
+               as_warn("BSS length (%d.) <0! Ignored.", temp);
+               ignore_rest_of_line();
+               return;
+       }
+       *p = 0;
+       symbolP = symbol_find_or_make (name);
+       *p = c;
+       if (   symbolP -> sy_other == 0
+           && symbolP -> sy_desc  == 0
+           && (   (   symbolP -> sy_type  == N_BSS
+           && symbolP -> sy_value == local_bss_counter)
+           || (   (symbolP -> sy_type & N_TYPE) == N_UNDF
+           && symbolP -> sy_value == 0))) {
+               symbolP -> sy_value = local_bss_counter;
+               symbolP -> sy_type  = N_BSS;
+               symbolP -> sy_frag  = & bss_address_frag;
+               local_bss_counter += temp;
+       } else
+               as_warn( "Ignoring attempt to re-define symbol from %d. to %d.",
+ symbolP -> sy_value, local_bss_counter );
+       demand_empty_rest_of_line();
+}
+
+void
+s_line()
+{
+       /* Assume delimiter is part of expression. */
+       /* BSD4.2 as fails with delightful bug, so we */
+       /* are not being incompatible here. */
+       new_logical_line ((char *)NULL, (int)(get_absolute_expression ()));
+       demand_empty_rest_of_line();
+}
+
+void
+s_long()
+{
+       cons(4);
+}
+
+void
+s_int()
+{
+       cons(4);
+}
+
+void
+s_lsym()
+{
+       register char *name;
+       register char c;
+       register char *p;
+       register segT segment;
+       expressionS exp;
+       register symbolS *symbolP;
+
+       /* we permit ANY expression: BSD4.2 demands constants */
+       name = input_line_pointer;
+       c = get_symbol_end();
+       p = input_line_pointer;
+       *p = c;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               *p = 0;
+               as_warn("Expected comma after name \"%s\"", name);
+               *p = c;
+               ignore_rest_of_line();
+               return;
+       }
+       input_line_pointer ++;
+       segment = expression (& exp);
+       if (   segment != SEG_ABSOLUTE && segment != SEG_DATA &&
+ segment != SEG_TEXT && segment != SEG_BSS) {
+               as_bad("Bad expression: %s", seg_name [(int)segment]);
+               ignore_rest_of_line();
+               return;
+       }
+ know(   segment == SEG_ABSOLUTE || segment == SEG_DATA || segment == SEG_TEXT || segment == SEG_BSS );
+       *p = 0;
+       symbolP = symbol_new (name,(unsigned char)(seg_N_TYPE [(int) segment]),
+ 0, 0, (valueT)(exp . X_add_number), & zero_address_frag);
+       *p = c;
+       demand_empty_rest_of_line();
+}
+
+void
+s_org()
+{
+       register segT segment;
+       expressionS exp;
+       register long int temp_fill;
+       register char *p;
+/*
+ * Don't believe the documentation of BSD 4.2 AS.
+ * There is no such thing as a sub-segment-relative origin.
+ * Any absolute origin is given a warning, then assumed to be segment-relative.
+ * Any segmented origin expression ("foo+42") had better be in the right
+ * segment or the .org is ignored.
+ *
+ * BSD 4.2 AS warns if you try to .org backwards. We cannot because we
+ * never know sub-segment sizes when we are reading code.
+ * BSD will crash trying to emit -ve numbers of filler bytes in certain
+ * .orgs. We don't crash, but see as-write for that code.
+ */
+/*
+ * Don't make frag if need_pass_2==TRUE.
+ */
+       segment = get_known_segmented_expression(& exp);
+       if ( *input_line_pointer == ',' ) {
+               input_line_pointer ++;
+               temp_fill = get_absolute_expression ();
+       } else
+               temp_fill = 0;
+       if ( ! need_pass_2 ) {
+               if (segment != now_seg && segment != SEG_ABSOLUTE)
+                       as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
+ seg_name [(int) segment], seg_name [(int) now_seg]);
+               p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp . X_add_symbol,
+ exp . X_add_number, (char *)0);
+               * p = temp_fill;
+       } /* if (ok to make frag) */
+       demand_empty_rest_of_line();
+}
+
+void
+s_set()
+{
+       register char *name;
+       register char delim;
+       register char *end_name;
+       register symbolS *symbolP;
+
+       /*
+        * Especial apologies for the random logic:
+        * this just grew, and could be parsed much more simply!
+        * Dean in haste.
+        */
+       name = input_line_pointer;
+       delim = get_symbol_end();
+       end_name = input_line_pointer;
+       *end_name = delim;
+       SKIP_WHITESPACE();
+       if ( * input_line_pointer != ',' ) {
+               *end_name = 0;
+               as_warn("Expected comma after name \"%s\"", name);
+               *end_name = delim;
+               ignore_rest_of_line();
+               return;
+       }
+       input_line_pointer ++;
+       *end_name = 0;
+       if(name[0]=='.' && name[1]=='\0') {
+         /* Turn '. = mumble' into a .org mumble */
+         register segT segment;
+         expressionS exp;
+         register char *ptr;
+
+         segment = get_known_segmented_expression(& exp);
+         if ( ! need_pass_2 ) {
+           if (segment != now_seg && segment != SEG_ABSOLUTE)
+             as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
+                     seg_name [(int) segment], seg_name [(int) now_seg]);
+           ptr = frag_var (rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol,
+                         exp.X_add_number, (char *)0);
+           *ptr= 0;
+         } /* if (ok to make frag) */
+         *end_name = delim;
+         return;
+       }
+       symbolP = symbol_find_or_make (name);
+       *end_name = delim;
+       pseudo_set (symbolP);
+       demand_empty_rest_of_line ();
+}
+
+void
+s_space()
+{
+       long int temp_repeat;
+       register long int temp_fill;
+       register char *p;
+
+       /* Just like .fill, but temp_size = 1 */
+       if ( get_absolute_expression_and_terminator( & temp_repeat) == ',' ) {
+               temp_fill = get_absolute_expression ();
+       } else {
+               input_line_pointer --; /* Backup over what was not a ','. */
+               temp_fill = 0;
+       }
+       if ( temp_repeat <= 0 ) {
+               as_warn("Repeat < 0, .space ignored");
+               ignore_rest_of_line();
+               return;
+       }
+       if ( ! need_pass_2 ) {
+               p = frag_var (rs_fill, 1, 1, (relax_substateT)0, (symbolS *)0,
+ temp_repeat, (char *)0);
+               * p = temp_fill;
+       }
+       demand_empty_rest_of_line();
+}
+
+void
+s_text()
+{
+       register int temp;
+
+       temp = get_absolute_expression ();
+       subseg_new (SEG_TEXT, (subsegT)temp);
+       demand_empty_rest_of_line();
+}
+
+\f
+/*( JF was static, but can't be if machine dependent pseudo-ops are to use it */
+
+void
+demand_empty_rest_of_line()
+{
+  SKIP_WHITESPACE();
+  if ( is_end_of_line [* input_line_pointer] )
+    {
+      input_line_pointer ++;
+    }
+  else
+    {
+      ignore_rest_of_line();
+    }
+                               /* Return having already swallowed end-of-line. */
+}                              /* Return pointing just after end-of-line. */
+
+
+void
+ignore_rest_of_line()          /* For suspect lines: gives warning. */
+{
+  if ( ! is_end_of_line [* input_line_pointer])
+    {
+      as_warn("Rest of line ignored. 1st junk character valued %d (%c)."
+             , * input_line_pointer, *input_line_pointer);
+      while (   input_line_pointer < buffer_limit
+            && ! is_end_of_line [* input_line_pointer] )
+       {
+         input_line_pointer ++;
+       }
+    }
+  input_line_pointer ++;       /* Return pointing just after end-of-line. */
+  know( is_end_of_line [input_line_pointer [-1]] );
+}
+\f
+/*
+ *                     stab()
+ *
+ * Handle .stabX directives, which used to be open-coded.
+ * So much creeping featurism overloaded the semantics that we decided
+ * to put all .stabX thinking in one place. Here.
+ *
+ * We try to make any .stabX directive legal. Other people's AS will often
+ * do assembly-time consistency checks: eg assigning meaning to n_type bits
+ * and "protecting" you from setting them to certain values. (They also zero
+ * certain bits before emitting symbols. Tut tut.)
+ *
+ * If an expression is not absolute we either gripe or use the relocation
+ * information. Other people's assemblers silently forget information they
+ * don't need and invent information they need that you didn't supply.
+ *
+ * .stabX directives always make a symbol table entry. It may be junk if
+ * the rest of your .stabX directive is malformed.
+ */
+static void
+stab (what)
+int what;
+{
+  register symbolS *   symbolP;
+  register char *      string;
+          int          saved_type;
+          int          length;
+          int          goof;   /* TRUE if we have aborted. */
+          long int     longint;
+
+/*
+ * Enter with input_line_pointer pointing past .stabX and any following
+ * whitespace.
+ */
+       goof = FALSE; /* JF who forgot this?? */
+       if (what == 's') {
+               string = demand_copy_C_string (& length);
+               SKIP_WHITESPACE();
+               if (* input_line_pointer == ',')
+                       input_line_pointer ++;
+               else {
+                       as_warn( "I need a comma after symbol's name" );
+                       goof = TRUE;
+               }
+       } else
+               string = "";
+
+/*
+ * Input_line_pointer->after ','.  String -> symbol name.
+ */
+       if (! goof) {
+               symbolP = symbol_new (string, 0,0,0,0,(struct frag *)0);
+               switch (what) {
+               case 'd':
+                       symbolP->sy_name = NULL; /* .stabd feature. */
+                       symbolP->sy_value = obstack_next_free(& frags) - frag_now->fr_literal;
+                       symbolP->sy_frag = frag_now;
+                       break;
+
+               case 'n':
+                       symbolP->sy_frag = &zero_address_frag;
+                       break;
+
+               case 's':
+                       symbolP->sy_frag = & zero_address_frag;
+                       break;
+
+               default:
+                       BAD_CASE( what );
+                       break;
+               }
+               if (get_absolute_expression_and_terminator (& longint) == ',')
+                       symbolP->sy_type = saved_type = longint;
+               else {
+                       as_warn( "I want a comma after the n_type expression" );
+                       goof = TRUE;
+                       input_line_pointer --; /* Backup over a non-',' char. */
+               }
+       }
+       if (! goof) {
+               if (get_absolute_expression_and_terminator (& longint) == ',')
+                       symbolP->sy_other = longint;
+               else {
+                       as_warn( "I want a comma after the n_other expression" );
+                       goof = TRUE;
+                       input_line_pointer --; /* Backup over a non-',' char. */
+               }
+       }
+       if (! goof) {
+               symbolP->sy_desc = get_absolute_expression ();
+               if (what == 's' || what == 'n') {
+                       if (* input_line_pointer != ',') {
+                               as_warn( "I want a comma after the n_desc expression" );
+                               goof = TRUE;
+                       } else {
+                               input_line_pointer ++;
+                       }
+               }
+       }
+       if ((! goof) && (what=='s' || what=='n')) {
+               pseudo_set (symbolP);
+               symbolP->sy_type = saved_type;
+       }
+       if (goof)
+               ignore_rest_of_line ();
+       else
+               demand_empty_rest_of_line ();
+}
+\f
+/*
+ *                     pseudo_set()
+ *
+ * In: Pointer to a symbol.
+ *     Input_line_pointer -> expression.
+ *
+ * Out:        Input_line_pointer -> just after any whitespace after expression.
+ *     Tried to set symbol to value of expression.
+ *     Will change sy_type, sy_value, sy_frag;
+ *     May set need_pass_2 == TRUE.
+ */
+static void
+pseudo_set (symbolP)
+     symbolS * symbolP;
+{
+  expressionS  exp;
+  register segT        segment;
+  int ext;
+
+  know( symbolP );             /* NULL pointer is logic error. */
+  ext=(symbolP->sy_type&N_EXT);
+  if ((segment = expression( & exp )) == SEG_NONE)
+    {
+      as_warn( "Missing expression: absolute 0 assumed" );
+      exp . X_seg              = SEG_ABSOLUTE;
+      exp . X_add_number       = 0;
+    }
+  switch (segment)
+    {
+    case SEG_BIG:
+      as_warn( "%s number illegal. Absolute 0 assumed.",
+             exp . X_add_number > 0 ? "Bignum" : "Floating-Point" );
+      symbolP -> sy_type = N_ABS | ext;
+      symbolP -> sy_value = 0;
+      symbolP -> sy_frag = & zero_address_frag;
+      break;
+
+    case SEG_NONE:
+      as_warn("No expression:  Using absolute 0");
+      symbolP -> sy_type = N_ABS | ext;
+      symbolP -> sy_value = 0;
+      symbolP -> sy_frag = & zero_address_frag;
+      break;
+
+    case SEG_DIFFERENCE:
+      if (exp.X_add_symbol && exp.X_subtract_symbol
+          &&    (exp.X_add_symbol->sy_type & N_TYPE)
+            == (exp.X_subtract_symbol->sy_type & N_TYPE)) {
+       if(exp.X_add_symbol->sy_frag != exp.X_subtract_symbol->sy_frag) {
+         as_bad("Unknown expression: symbols %s and %s are in different frags.",exp.X_add_symbol->sy_name, exp.X_subtract_symbol->sy_name);
+         need_pass_2++;
+       }
+       exp.X_add_number+=exp.X_add_symbol->sy_value - exp.X_subtract_symbol->sy_value;
+      } else
+       as_warn( "Complex expression. Absolute segment assumed." );
+    case SEG_ABSOLUTE:
+      symbolP -> sy_type = N_ABS | ext;
+      symbolP -> sy_value = exp . X_add_number;
+      symbolP -> sy_frag = & zero_address_frag;
+      break;
+    case SEG_DATA:
+    case SEG_TEXT:
+    case SEG_BSS:
+      symbolP -> sy_type = seg_N_TYPE [(int) segment] | ext;
+      symbolP -> sy_value= exp . X_add_number + exp . X_add_symbol -> sy_value;
+      symbolP -> sy_frag = exp . X_add_symbol -> sy_frag;
+      break;
+      
+    case SEG_PASS1:            /* Not an error. Just try another pass. */
+      symbolP->sy_forward=exp.X_add_symbol;
+      as_warn("Unknown expression");
+      know( need_pass_2 == TRUE );
+      break;
+      
+    case SEG_UNKNOWN:
+      symbolP->sy_forward=exp.X_add_symbol;
+      /* as_warn("unknown symbol"); */
+      /* need_pass_2 = TRUE; */
+      break;
+      
+    default:
+      BAD_CASE( segment );
+      break;
+    }
+}
+\f
+/*
+ * stabs(file), stabf(func) and stabd(line) -- for the purpose of
+ * source file debugging of assembly files, generate file,
+ * function and line number stabs, respectively.
+ * These functions have corresponding functions named
+ * filestab(), funcstab() and linestab() in input-scrub.c,
+ * where logical files and logical line numbers are handled.
+ */
+
+#include <stab.h>
+
+stabs(file)
+     char *file;
+{
+  /* .stabs "file",100,0,0,. */
+  (void) symbol_new(file,
+                   N_SO,
+                   0,
+                   0,
+                   obstack_next_free(& frags) - frag_now->fr_literal,
+                   frag_now);
+}
+
+stabf(func)
+     char *func;
+{
+  symbolS *symbolP;
+  static int void_undefined = 1;
+
+  /* crudely filter uninteresting labels: require an initial '_' */
+  if (*func++ != '_')
+    return;
+
+  /* assembly functions are assumed to have void type */
+  if (void_undefined)
+    {
+      /* .stabs "void:t15=15",128,0,0,0 */
+      (void) symbol_new("void:t1=1",
+                       N_LSYM,
+                       0,
+                       0,
+                       0,
+                       &zero_address_frag);
+      void_undefined = 0;
+    }
+
+  /* .stabs "func:F1",36,0,0,. */
+  symbolP = symbol_new((char *) 0,
+                      N_FUN,
+                      0,
+                      0,
+                      obstack_next_free(& frags) - frag_now->fr_literal,
+                      frag_now);
+  obstack_grow(&notes, func, strlen(func));
+  obstack_1grow(&notes, ':');
+  obstack_1grow(&notes, 'F');
+  obstack_1grow(&notes, '1');
+  obstack_1grow(&notes, '\0');
+  symbolP->sy_name = obstack_finish(&notes);
+}
+
+stabd(line)
+     unsigned line;
+{
+  /* .stabd 68,0,line */
+  (void) symbol_new((char *)0,
+                   N_SLINE,
+                   0,
+                   line,
+                   obstack_next_free(& frags) - frag_now->fr_literal,
+                   frag_now);
+}
+\f
+/*
+ *                     cons()
+ *
+ * CONStruct more frag of .bytes, or .words etc.
+ * Should need_pass_2 be TRUE then emit no frag(s).
+ * This understands EXPRESSIONS, as opposed to big_cons().
+ *
+ * Bug (?)
+ *
+ * This has a split personality. We use expression() to read the
+ * value. We can detect if the value won't fit in a byte or word.
+ * But we can't detect if expression() discarded significant digits
+ * in the case of a long. Not worth the crocks required to fix it.
+ */
+void
+cons(nbytes)                   /* worker to do .byte etc statements */
+                               /* clobbers input_line_pointer, checks */
+                               /* end-of-line. */
+     register int      nbytes; /* 1=.byte, 2=.word, 4=.long */
+{
+  register char                c;
+  register long int    mask;   /* High-order bits we will left-truncate, */
+                               /* but includes sign bit also. */
+  register long int     get;   /* what we get */
+  register long int    use;    /* get after truncation. */
+  register long int    unmask; /* what bits we will store */
+  register char *      p;
+  register segT                segment;
+           expressionS exp;
+#ifdef NS32K
+  void fix_new_ns32k();
+#else
+  void fix_new();
+#endif
+
+  /*
+   * Input_line_pointer -> 1st char after pseudo-op-code and could legally
+   * be a end-of-line. (Or, less legally an eof - which we cope with.)
+   */
+  /* JF << of >= number of bits in the object is undefined.  In particular
+     SPARC (Sun 4) has problems */
+  if(nbytes>=sizeof(long int))
+    mask = 0;
+  else 
+    mask = ~0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */
+  unmask = ~ mask;             /* Do store these bits. */
+#ifdef NEVER
+  "Do this mod if you want every overflow check to assume SIGNED 2's complement data.";
+  mask = ~ (unmask >> 1);      /* Includes sign bit now. */
+#endif
+  /*
+   * The following awkward logic is to parse ZERO or more expressions,
+   * comma seperated. Recall an expression includes its leading &
+   * trailing blanks. We fake a leading ',' if there is (supposed to
+   * be) a 1st expression, and keep demanding 1 expression for each ','.
+   */
+  if (is_it_end_of_statement())
+    {
+      c = 0;                   /* Skip loop. */
+      input_line_pointer ++;   /* Matches end-of-loop 'correction'. */
+    }
+  else
+      c = ',';                 /* Do loop. */
+  while ( c == ','  )
+    {
+      segment = expression( &exp ); /* At least scan over the expression. */
+      if ( ! need_pass_2 )
+       {                       /* Still worthwhile making frags. */
+
+         /* Don't call this if we are going to junk this pass anyway! */
+         know( segment != SEG_PASS1 );
+
+         if ( segment == SEG_DIFFERENCE && exp . X_add_symbol == NULL )
+           {
+             as_warn( "Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.",
+                     exp . X_subtract_symbol -> sy_name,
+                     seg_name [(int) N_TYPE_seg [exp . X_subtract_symbol -> sy_type & N_TYPE]]);
+             segment = SEG_ABSOLUTE;
+             /* Leave exp . X_add_number alone. */
+           }
+         p = frag_more (nbytes);
+         switch (segment)
+           {
+           case SEG_BIG:
+             as_warn( "%s number illegal. Absolute 0 assumed.",
+                     exp . X_add_number > 0 ? "Bignum" : "Floating-Point");
+             md_number_to_chars (p, (long)0, nbytes);
+             break;
+
+           case SEG_NONE:
+             as_warn( "0 assumed for missing expression" );
+             exp . X_add_number = 0;
+             know( exp . X_add_symbol == NULL );
+             /* fall into SEG_ABSOLUTE */
+           case SEG_ABSOLUTE:
+             get = exp . X_add_number;
+             use = get & unmask;
+             if ( (get & mask) && (get & mask) != mask )
+               {               /* Leading bits contain both 0s & 1s. */
+                 as_warn("Value x%x truncated to x%x.", get, use);
+               }
+             md_number_to_chars (p, use, nbytes); /* put bytes in right order. */
+             break;
+
+           case SEG_DIFFERENCE:
+#ifndef WORKING_DOT_WORD
+             if(nbytes==2) {
+               struct broken_word *x;
+
+               x=(struct broken_word *)xmalloc(sizeof(struct broken_word));
+               x->next_broken_word=broken_words;
+               broken_words=x;
+               x->frag=frag_now;
+               x->word_goes_here=p;
+               x->dispfrag=0;
+               x->add=exp.X_add_symbol;
+               x->sub=exp.X_subtract_symbol;
+               x->addnum=exp.X_add_number;
+               x->added=0;
+               new_broken_words++;
+               break;
+             }
+             /* Else Fall through into. . . */
+#endif
+           case SEG_BSS:
+           case SEG_UNKNOWN:
+           case SEG_TEXT:
+           case SEG_DATA:
+#if defined(SPARC) || defined(I860)
+             fix_new (frag_now, p - frag_now -> fr_literal, nbytes,
+                      exp . X_add_symbol, exp . X_subtract_symbol,
+                      exp . X_add_number, 0, RELOC_32);
+#endif
+#ifdef NS32K
+             fix_new_ns32k (frag_now, p - frag_now -> fr_literal, nbytes,
+                      exp . X_add_symbol, exp . X_subtract_symbol,
+                      exp . X_add_number, 0, 0, 2, 0, 0);
+#endif
+#if !defined(SPARC) && !defined(NS32K) && !defined(I860)
+             fix_new (frag_now, p - frag_now -> fr_literal, nbytes,
+                      exp . X_add_symbol, exp . X_subtract_symbol,
+                      exp . X_add_number, 0);
+#endif
+             break;
+
+           default:
+             BAD_CASE( segment );
+             break;
+           }                   /* switch(segment) */
+       }                       /* if(!need_pass_2) */
+      c = * input_line_pointer ++;
+    }                          /* while(c==',') */
+  input_line_pointer --;       /* Put terminator back into stream. */
+  demand_empty_rest_of_line();
+}                              /* cons() */
+\f
+/*
+ *                     big_cons()
+ *
+ * CONStruct more frag(s) of .quads, or .octa etc.
+ * Makes 0 or more new frags.
+ * If need_pass_2 == TRUE, generate no frag.
+ * This understands only bignums, not expressions. Cons() understands
+ * expressions.
+ *
+ * Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal).
+ *
+ * This creates objects with struct obstack_control objs, destroying
+ * any context objs held about a partially completed object. Beware!
+ *
+ *
+ * I think it sucks to have 2 different types of integers, with 2
+ * routines to read them, store them etc.
+ * It would be nicer to permit bignums in expressions and only
+ * complain if the result overflowed. However, due to "efficiency"...
+ */
+void
+big_cons(nbytes)               /* worker to do .quad etc statements */
+                               /* clobbers input_line_pointer, checks */
+                               /* end-of-line. */
+     register int      nbytes; /* 8=.quad 16=.octa ... */
+{
+  register char                c;      /* input_line_pointer -> c. */
+  register int         radix;
+  register long int    length; /* Number of chars in an object. */
+  register int         digit;  /* Value of 1 digit. */
+  register int         carry;  /* For multi-precision arithmetic. */
+  register int         work;   /* For multi-precision arithmetic. */
+  register char *      p;      /* For multi-precision arithmetic. */
+
+  extern char hex_value[];     /* In hex_value.c. */
+
+  /*
+   * The following awkward logic is to parse ZERO or more strings,
+   * comma seperated. Recall an expression includes its leading &
+   * trailing blanks. We fake a leading ',' if there is (supposed to
+   * be) a 1st expression, and keep demanding 1 expression for each ','.
+   */
+  if (is_it_end_of_statement())
+    {
+      c = 0;                   /* Skip loop. */
+    }
+  else
+    {
+      c = ',';                 /* Do loop. */
+      -- input_line_pointer;
+    }
+  while (c == ',')
+    {
+      ++ input_line_pointer;
+      SKIP_WHITESPACE();
+      c = * input_line_pointer;
+      /* C contains 1st non-blank character of what we hope is a number. */
+      if (c == '0')
+       {
+         c = * ++ input_line_pointer;
+         if (c == 'x' || c=='X')
+           {
+             c = * ++ input_line_pointer;
+             radix = 16;
+           }
+         else
+           {
+             radix = 8;
+           }
+       }
+      else
+       {
+         radix = 10;
+       }
+      /*
+       * This feature (?) is here to stop people worrying about
+       * mysterious zero constants: which is what they get when
+       * they completely omit digits.
+       */
+      if (hex_value[c] >= radix)
+       {
+         as_warn( "Missing digits. 0 assumed." );
+       }
+      bignum_high = bignum_low - 1; /* Start constant with 0 chars. */
+      for(   ;   (digit = hex_value [c]) < radix;   c = * ++ input_line_pointer)
+       {
+         /* Multiply existing number by radix, then add digit. */
+         carry = digit;
+         for (p=bignum_low;   p <= bignum_high;   p++)
+           {
+             work = (*p & MASK_CHAR) * radix + carry;
+             *p = work & MASK_CHAR;
+             carry = work >> BITS_PER_CHAR;
+           }
+         if (carry)
+           {
+             grow_bignum();
+             * bignum_high = carry & MASK_CHAR;
+             know( (carry & ~ MASK_CHAR) == 0);
+           }
+       }
+      length = bignum_high - bignum_low + 1;
+      if (length > nbytes)
+       {
+         as_warn( "Most significant bits truncated in integer constant." );
+       }
+      else
+       {
+         register long int     leading_zeroes;
+
+         for(leading_zeroes = nbytes - length;
+             leading_zeroes;
+             leading_zeroes --)
+           {
+             grow_bignum();
+             * bignum_high = 0;
+           }
+       }
+      if (! need_pass_2)
+       {
+         p = frag_more (nbytes);
+         bcopy (bignum_low, p, (int)nbytes);
+       }
+      /* C contains character after number. */
+      SKIP_WHITESPACE();
+      c = * input_line_pointer;
+      /* C contains 1st non-blank character after number. */
+    }
+  demand_empty_rest_of_line();
+}                              /* big_cons() */
+
+static void
+grow_bignum()                  /* Extend bignum by 1 char. */
+{
+  register long int    length;
+
+  bignum_high ++;
+  if (bignum_high >= bignum_limit)
+    {
+      length = bignum_limit - bignum_low;
+      bignum_low = xrealloc (bignum_low, length + length);
+      bignum_high = bignum_low + length;
+      bignum_limit = bignum_low + length + length;
+    }
+}                              /* grow_bignum(); */
+\f
+/*
+ *                     float_cons()
+ *
+ * CONStruct some more frag chars of .floats .ffloats etc.
+ * Makes 0 or more new frags.
+ * If need_pass_2 == TRUE, no frags are emitted.
+ * This understands only floating literals, not expressions. Sorry.
+ *
+ * A floating constant is defined by atof_generic(), except it is preceded
+ * by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its
+ * reading, I decided to be incompatible. This always tries to give you
+ * rounded bits to the precision of the pseudo-op. Former AS did premature
+ * truncatation, restored noisy bits instead of trailing 0s AND gave you
+ * a choice of 2 flavours of noise according to which of 2 floating-point
+ * scanners you directed AS to use.
+ *
+ * In: input_line_pointer -> whitespace before, or '0' of flonum.
+ *
+ */
+
+void   /* JF was static, but can't be if VAX.C is goning to use it */
+float_cons(float_type)         /* Worker to do .float etc statements. */
+                               /* Clobbers input_line-pointer, checks end-of-line. */
+     register float_type;      /* 'f':.ffloat ... 'F':.float ... */
+{
+  register char *      p;
+  register char                c;
+  int  length; /* Number of chars in an object. */
+  register char *      err;    /* Error from scanning floating literal. */
+  char         temp [MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT];
+
+  /*
+   * The following awkward logic is to parse ZERO or more strings,
+   * comma seperated. Recall an expression includes its leading &
+   * trailing blanks. We fake a leading ',' if there is (supposed to
+   * be) a 1st expression, and keep demanding 1 expression for each ','.
+   */
+  if (is_it_end_of_statement())
+    {
+      c = 0;                   /* Skip loop. */
+      ++ input_line_pointer;   /* -> past termintor. */
+    }
+  else
+    {
+      c = ',';                 /* Do loop. */
+    }
+  while (c == ',')
+    {
+      /* input_line_pointer -> 1st char of a flonum (we hope!). */
+      SKIP_WHITESPACE();
+      /* Skip any 0{letter} that may be present. Don't even check if the
+       * letter is legal. Someone may invent a "z" format and this routine
+       * has no use for such information. Lusers beware: you get
+       * diagnostics if your input is ill-conditioned.
+       */
+
+      if(input_line_pointer[0]=='0' && isalpha(input_line_pointer[1]))
+         input_line_pointer+=2;
+
+      err = md_atof (float_type, temp, &length);
+      know( length <=  MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
+      know( length > 0 );
+      if (* err)
+       {
+         as_warn( "Bad floating literal: %s", err);
+         ignore_rest_of_line();
+         /* Input_line_pointer -> just after end-of-line. */
+         c = 0;                /* Break out of loop. */
+       }
+      else
+       {
+         if ( ! need_pass_2)
+           {
+             p = frag_more (length);
+             bcopy (temp, p, length);
+           }
+         SKIP_WHITESPACE();
+         c = * input_line_pointer ++;
+         /* C contains 1st non-white character after number. */
+         /* input_line_pointer -> just after terminator (c). */
+       }
+    }
+  -- input_line_pointer;               /* -> terminator (is not ','). */
+  demand_empty_rest_of_line();
+}                              /* float_cons() */
+\f
+/*
+ *                     stringer()
+ *
+ * We read 0 or more ',' seperated, double-quoted strings.
+ *
+ * Caller should have checked need_pass_2 is FALSE because we don't check it.
+ */
+static void
+stringer(append_zero)          /* Worker to do .ascii etc statements. */
+                               /* Checks end-of-line. */
+     register int append_zero; /* 0: don't append '\0', else 1 */
+{
+  /* register char *   p; JF unused */
+  /* register int              length; JF unused */    /* Length of string we read, excluding */
+                               /* trailing '\0' implied by closing quote. */
+  /* register char *   where; JF unused */
+  /* register fragS *  fragP; JF unused */
+  register int c;
+
+  /*
+   * The following awkward logic is to parse ZERO or more strings,
+   * comma seperated. Recall a string expression includes spaces
+   * before the opening '\"' and spaces after the closing '\"'.
+   * We fake a leading ',' if there is (supposed to be)
+   * a 1st, expression. We keep demanding expressions for each
+   * ','.
+   */
+  if (is_it_end_of_statement())
+    {
+      c = 0;                   /* Skip loop. */
+      ++ input_line_pointer;   /* Compensate for end of loop. */
+    }
+  else
+    {
+      c = ',';                 /* Do loop. */
+    }
+  for (   ;   c == ',';   c = *input_line_pointer ++)
+    {
+      SKIP_WHITESPACE();
+      if (* input_line_pointer == '\"')
+       {
+         ++ input_line_pointer; /* -> 1st char of string. */
+         while ( (c = next_char_of_string()) >= 0)
+           {
+             FRAG_APPEND_1_CHAR( c );
+           }
+         if (append_zero)
+           {
+             FRAG_APPEND_1_CHAR( 0 );
+           }
+         know( input_line_pointer [-1] == '\"' );
+       }
+      else
+       {
+         as_warn( "Expected \"-ed string" );
+       }
+      SKIP_WHITESPACE();
+    }
+  -- input_line_pointer;
+  demand_empty_rest_of_line();
+}                              /* stringer() */
+\f
+static int
+next_char_of_string ()
+{
+  register int c;
+
+  c = * input_line_pointer ++;
+  switch (c)
+    {
+    case '\"':
+      c = -1;
+      break;
+
+    case '\\':
+      switch (c = * input_line_pointer ++)
+       {
+       case 'b':
+         c = '\b';
+         break;
+
+       case 'f':
+         c = '\f';
+         break;
+
+       case 'n':
+         c = '\n';
+         break;
+
+       case 'r':
+         c = '\r';
+         break;
+
+       case 't':
+         c = '\t';
+         break;
+
+       case '\\':
+       case '"':
+         break;                /* As itself. */
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         {
+           long int number;
+
+           for (number = 0;   isdigit(c);   c = * input_line_pointer ++)
+             {
+               number = number * 8 + c - '0';
+             }
+           c = number;
+         }
+         -- input_line_pointer;
+         break;
+
+       case '\n':
+/*       as_fatal( "Unterminated string - use app!" ); */
+/* To be compatible with BSD 4.2 as: give the luser a linefeed!! */
+         c = '\n';
+         break;
+
+       default:
+         as_warn( "Bad escaped character in string, '?' assumed" );
+         c = '?';
+         break;
+       }
+      break;
+
+    default:
+      break;
+    }
+  return (c);
+}
+\f
+static segT
+get_segmented_expression ( expP )
+     register expressionS *    expP;
+{
+  register segT                retval;
+
+  if ( (retval = expression( expP )) == SEG_PASS1 || retval == SEG_NONE || retval == SEG_BIG )
+    {
+      as_warn("Expected address expression: absolute 0 assumed");
+      retval = expP -> X_seg = SEG_ABSOLUTE;
+      expP -> X_add_number   = 0;
+      expP -> X_add_symbol   = expP -> X_subtract_symbol = 0;
+    }
+  return (retval);             /* SEG_ ABSOLUTE,UNKNOWN,DATA,TEXT,BSS */
+}
+
+static segT
+get_known_segmented_expression ( expP )
+     register expressionS *    expP;
+{
+  register segT                retval;
+  register char *      name1;
+  register char *      name2;
+
+  if (   (retval = get_segmented_expression (expP)) == SEG_UNKNOWN
+      )
+    {
+      name1 = expP -> X_add_symbol ? expP -> X_add_symbol -> sy_name : "";
+      name2 = expP -> X_subtract_symbol ? expP -> X_subtract_symbol -> sy_name : "";
+      if ( name1 && name2 )
+       {
+         as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 assumed.",
+                 name1, name2);
+       }
+      else
+       {
+         as_warn("Symbol \"%s\" undefined: absolute 0 assumed.",
+                 name1 ? name1 : name2);
+       }
+      retval = expP -> X_seg = SEG_ABSOLUTE;
+      expP -> X_add_number   = 0;
+      expP -> X_add_symbol   = expP -> X_subtract_symbol = NULL;
+    }
+ know(   retval == SEG_ABSOLUTE || retval == SEG_DATA || retval == SEG_TEXT || retval == SEG_BSS || retval == SEG_DIFFERENCE );
+  return (retval);
+}                              /* get_known_segmented_expression() */
+
+
+
+/* static */ long int /* JF was static, but can't be if the MD pseudos are to use it */
+get_absolute_expression ()
+{
+  expressionS  exp;
+  register segT s;
+
+  if ( (s = expression(& exp)) != SEG_ABSOLUTE )
+    {
+      if ( s != SEG_NONE )
+       {
+         as_warn( "Bad Absolute Expression, absolute 0 assumed.");
+       }
+      exp . X_add_number = 0;
+    }
+  return (exp . X_add_number);
+}
+
+static char                    /* return terminator */
+get_absolute_expression_and_terminator( val_pointer)
+     long int *                val_pointer; /* return value of expression */
+{
+  * val_pointer = get_absolute_expression ();
+  return ( * input_line_pointer ++ );
+}
+\f
+/*
+ *                     demand_copy_C_string()
+ *
+ * Like demand_copy_string, but return NULL if the string contains any '\0's.
+ * Give a warning if that happens.
+ */
+static char *
+demand_copy_C_string (len_pointer)
+     int *     len_pointer;
+{
+  register char *      s;
+
+  if (s = demand_copy_string (len_pointer))
+    {
+      register int     len;
+
+      for (len = * len_pointer;
+          len > 0;
+          len--)
+       {
+         if (* s == 0)
+           {
+             s = 0;
+             len = 1;
+             * len_pointer = 0;
+             as_warn( "This string may not contain \'\\0\'" );
+           }
+       }
+    }
+  return (s);
+}
+\f
+/*
+ *                     demand_copy_string()
+ *
+ * Demand string, but return a safe (=private) copy of the string.
+ * Return NULL if we can't read a string here.
+ */
+static char *
+demand_copy_string (lenP)
+     int *     lenP;
+{
+  register int         c;
+  register int         len;
+          char *       retval;
+
+  len = 0;
+  SKIP_WHITESPACE();
+  if (* input_line_pointer == '\"')
+    {
+      input_line_pointer ++;   /* Skip opening quote. */
+      while ( (c = next_char_of_string()) >= 0 ) {
+         obstack_1grow ( &notes, c );
+         len ++;
+       }
+      /* JF this next line is so demand_copy_C_string will return a null
+         termanated string. */
+      obstack_1grow(&notes,'\0');
+      retval=obstack_finish( &notes);
+  } else {
+      as_warn( "Missing string" );
+      retval = NULL;
+      ignore_rest_of_line ();
+    }
+  * lenP = len;
+  return (retval);
+}
+\f
+/*
+ *             is_it_end_of_statement()
+ *
+ * In: Input_line_pointer -> next character.
+ *
+ * Do: Skip input_line_pointer over all whitespace.
+ *
+ * Out:        TRUE if input_line_pointer -> end-of-line.
+ */
+static int
+is_it_end_of_statement()
+{
+  SKIP_WHITESPACE();
+  return (is_end_of_line [* input_line_pointer]);
+}
+
+void
+equals(sym_name)
+char *sym_name;
+{
+  register struct symbol * symbolP; /* symbol we are working with */
+
+  input_line_pointer++;
+  if(*input_line_pointer=='=')
+    input_line_pointer++;
+
+  while(*input_line_pointer==' ' || *input_line_pointer=='\t')
+    input_line_pointer++;
+
+  if(sym_name[0]=='.' && sym_name[1]=='\0') {
+    /* Turn '. = mumble' into a .org mumble */
+    register segT segment;
+    expressionS exp;
+    register char *p;
+
+    segment = get_known_segmented_expression(& exp);
+    if ( ! need_pass_2 ) {
+      if (segment != now_seg && segment != SEG_ABSOLUTE)
+        as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.",
+                seg_name [(int) segment], seg_name [(int) now_seg]);
+      p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol,
+                    exp.X_add_number, (char *)0);
+      * p = 0;
+    } /* if (ok to make frag) */
+  } else {
+    symbolP=symbol_find_or_make(sym_name);
+    pseudo_set(symbolP);
+  }
+}
+
+/* end: read.c */