mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-11 13:02:10 +08:00
I noticed the regenerated BFD_RELOC_MICROBLAZE_32_NONE comment didn't match that committed to bfd-in2.h, and was just going to regen bfd-in2.h but then decided to do something about the silly formatting of these comments in bfd-in2.h. eg. the BFD_RELOC_MICROBLAZE_32_NONE comment: -/* This is a 32 bit reloc that stores the 32 bit pc relative -value in two words (with an imm instruction).No relocation is -done here - only used for relaxing */ + /* This is a 32 bit reloc that stores the 32 bit pc relative value in + two words (with an imm instruction). No relocation is done here - + only used for relaxing. */ BFD_RELOC_MICROBLAZE_32_NONE, You'll notice how the second and third line of the original comment aren't indented properly relative to the first line, and the whole comment needs to be indented to match the code. I've also edited reloc.c ENUMDOC paragraphs. Some of these had excess indentation, presumably in an attempt to properly indent bfd-in2.h comments but that fails due to chew.c removing leading whitespace early by skip_white_and_stars. COMMENT was used in reloc.c to add extra blank lines in bfd-in2.h. I've removed them too as I don't think they add anything to readability of that file. (Perhaps more usefully, they also add blank lines to libbfd.h separating relocs for one target from others, but this isn't done consistently.) * doc/chew.c (drop, idrop): Move earlier. (strip_trailing_newlines): Check index before accessing array, not after. (wrap_comment): New function. (main): Add "wrap_comment" intrinsic. * doc/proto.str (ENUMDOC): Use wrap_comment. (make_enum_header, ENDSENUM): Put start and end braces on separate lines. * reloc.c: Remove uses of COMMENT and edit ENUMDOC paragraphs. * libbfd.h: Regenerate. * bfd-in2.h: Regenerate.
1636 lines
29 KiB
C
1636 lines
29 KiB
C
/* chew
|
|
Copyright (C) 1990-2023 Free Software Foundation, Inc.
|
|
Contributed by steve chamberlain @cygnus
|
|
|
|
This file is part of BFD, the Binary File Descriptor library.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
/* Yet another way of extracting documentation from source.
|
|
No, I haven't finished it yet, but I hope you people like it better
|
|
than the old way
|
|
|
|
sac
|
|
|
|
Basically, this is a sort of string forth, maybe we should call it
|
|
struth?
|
|
|
|
You define new words thus:
|
|
: <newword> <oldwords> ;
|
|
|
|
Variables are defined using:
|
|
variable NAME
|
|
|
|
*/
|
|
|
|
/* Primitives provided by the program:
|
|
|
|
Two stacks are provided, a string stack and an integer stack.
|
|
|
|
Internal state variables:
|
|
internal_wanted - indicates whether `-i' was passed
|
|
internal_mode - user-settable
|
|
|
|
Commands:
|
|
push_text
|
|
! - pop top of integer stack for address, pop next for value; store
|
|
@ - treat value on integer stack as the address of an integer; push
|
|
that integer on the integer stack after popping the "address"
|
|
hello - print "hello\n" to stdout
|
|
stdout - put stdout marker on TOS
|
|
stderr - put stderr marker on TOS
|
|
print - print TOS-1 on TOS (eg: "hello\n" stdout print)
|
|
skip_past_newline
|
|
catstr - fn icatstr
|
|
copy_past_newline - append input, up to and including newline into TOS
|
|
dup - fn other_dup
|
|
drop - discard TOS
|
|
idrop - ditto
|
|
remchar - delete last character from TOS
|
|
get_stuff_in_command
|
|
do_fancy_stuff - translate <<foo>> to @code{foo} in TOS
|
|
bulletize - if "o" lines found, prepend @itemize @bullet to TOS
|
|
and @item to each "o" line; append @end itemize
|
|
courierize - put @example around . and | lines, translate {* *} { }
|
|
exit - fn chew_exit
|
|
swap
|
|
outputdots - strip out lines without leading dots
|
|
maybecatstr - do catstr if internal_mode == internal_wanted, discard
|
|
value in any case
|
|
catstrif - do catstr if top of integer stack is nonzero
|
|
translatecomments - turn {* and *} into comment delimiters
|
|
kill_bogus_lines - get rid of extra newlines
|
|
indent
|
|
print_stack_level - print current stack depth to stderr
|
|
strip_trailing_newlines - go ahead, guess...
|
|
[quoted string] - push string onto string stack
|
|
[word starting with digit] - push atol(str) onto integer stack
|
|
|
|
internalmode - the internalmode variable (evaluates to address)
|
|
|
|
A command must be all upper-case, and alone on a line.
|
|
|
|
Foo. */
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
|
|
#define DEF_SIZE 5000
|
|
#define STACK 50
|
|
|
|
/* Here is a string type ... */
|
|
|
|
typedef struct buffer
|
|
{
|
|
char *ptr;
|
|
unsigned long write_idx;
|
|
unsigned long size;
|
|
} string_type;
|
|
|
|
/* Compiled programs consist of arrays of these. */
|
|
|
|
typedef union
|
|
{
|
|
void (*f) (void);
|
|
struct dict_struct *e;
|
|
char *s;
|
|
intptr_t l;
|
|
} pcu;
|
|
|
|
typedef struct dict_struct
|
|
{
|
|
char *word;
|
|
struct dict_struct *next;
|
|
pcu *code;
|
|
int code_length;
|
|
int code_end;
|
|
} dict_type;
|
|
|
|
int internal_wanted;
|
|
intptr_t *internal_mode;
|
|
|
|
int warning;
|
|
|
|
string_type stack[STACK];
|
|
string_type *tos;
|
|
|
|
unsigned int idx = 0; /* Pos in input buffer */
|
|
string_type *ptr; /* and the buffer */
|
|
|
|
intptr_t istack[STACK];
|
|
intptr_t *isp = &istack[0];
|
|
|
|
dict_type *root;
|
|
|
|
pcu *pc;
|
|
|
|
static void
|
|
die (char *msg)
|
|
{
|
|
fprintf (stderr, "%s\n", msg);
|
|
exit (1);
|
|
}
|
|
|
|
void *
|
|
xmalloc (size_t size)
|
|
{
|
|
void *newmem;
|
|
|
|
if (size == 0)
|
|
size = 1;
|
|
newmem = malloc (size);
|
|
if (!newmem)
|
|
die ("out of memory");
|
|
|
|
return newmem;
|
|
}
|
|
|
|
void *
|
|
xrealloc (void *oldmem, size_t size)
|
|
{
|
|
void *newmem;
|
|
|
|
if (size == 0)
|
|
size = 1;
|
|
if (!oldmem)
|
|
newmem = malloc (size);
|
|
else
|
|
newmem = realloc (oldmem, size);
|
|
if (!newmem)
|
|
die ("out of memory");
|
|
|
|
return newmem;
|
|
}
|
|
|
|
char *
|
|
xstrdup (const char *s)
|
|
{
|
|
size_t len = strlen (s) + 1;
|
|
char *ret = xmalloc (len);
|
|
return memcpy (ret, s, len);
|
|
}
|
|
|
|
static void
|
|
init_string_with_size (string_type *buffer, unsigned int size)
|
|
{
|
|
buffer->write_idx = 0;
|
|
buffer->size = size;
|
|
buffer->ptr = xmalloc (size);
|
|
}
|
|
|
|
static void
|
|
init_string (string_type *buffer)
|
|
{
|
|
init_string_with_size (buffer, DEF_SIZE);
|
|
}
|
|
|
|
static int
|
|
find (string_type *str, char *what)
|
|
{
|
|
unsigned int i;
|
|
char *p;
|
|
p = what;
|
|
for (i = 0; i < str->write_idx && *p; i++)
|
|
{
|
|
if (*p == str->ptr[i])
|
|
p++;
|
|
else
|
|
p = what;
|
|
}
|
|
return (*p == 0);
|
|
}
|
|
|
|
static void
|
|
write_buffer (string_type *buffer, FILE *f)
|
|
{
|
|
if (buffer->write_idx != 0
|
|
&& fwrite (buffer->ptr, buffer->write_idx, 1, f) != 1)
|
|
die ("cannot write output");
|
|
}
|
|
|
|
static void
|
|
delete_string (string_type *buffer)
|
|
{
|
|
free (buffer->ptr);
|
|
buffer->ptr = NULL;
|
|
}
|
|
|
|
static char *
|
|
addr (string_type *buffer, unsigned int idx)
|
|
{
|
|
return buffer->ptr + idx;
|
|
}
|
|
|
|
static char
|
|
at (string_type *buffer, unsigned int pos)
|
|
{
|
|
if (pos >= buffer->write_idx)
|
|
return 0;
|
|
return buffer->ptr[pos];
|
|
}
|
|
|
|
static void
|
|
catchar (string_type *buffer, int ch)
|
|
{
|
|
if (buffer->write_idx == buffer->size)
|
|
{
|
|
buffer->size *= 2;
|
|
buffer->ptr = xrealloc (buffer->ptr, buffer->size);
|
|
}
|
|
|
|
buffer->ptr[buffer->write_idx++] = ch;
|
|
}
|
|
|
|
static void
|
|
overwrite_string (string_type *dst, string_type *src)
|
|
{
|
|
free (dst->ptr);
|
|
dst->size = src->size;
|
|
dst->write_idx = src->write_idx;
|
|
dst->ptr = src->ptr;
|
|
}
|
|
|
|
static void
|
|
catbuf (string_type *buffer, char *buf, unsigned int len)
|
|
{
|
|
if (buffer->write_idx + len >= buffer->size)
|
|
{
|
|
while (buffer->write_idx + len >= buffer->size)
|
|
buffer->size *= 2;
|
|
buffer->ptr = xrealloc (buffer->ptr, buffer->size);
|
|
}
|
|
memcpy (buffer->ptr + buffer->write_idx, buf, len);
|
|
buffer->write_idx += len;
|
|
}
|
|
|
|
static void
|
|
cattext (string_type *buffer, char *string)
|
|
{
|
|
catbuf (buffer, string, (unsigned int) strlen (string));
|
|
}
|
|
|
|
static void
|
|
catstr (string_type *dst, string_type *src)
|
|
{
|
|
catbuf (dst, src->ptr, src->write_idx);
|
|
}
|
|
|
|
static unsigned int
|
|
skip_white_and_stars (string_type *src, unsigned int idx)
|
|
{
|
|
char c;
|
|
while ((c = at (src, idx)),
|
|
isspace ((unsigned char) c)
|
|
|| (c == '*'
|
|
/* Don't skip past end-of-comment or star as first
|
|
character on its line. */
|
|
&& at (src, idx +1) != '/'
|
|
&& at (src, idx -1) != '\n'))
|
|
idx++;
|
|
return idx;
|
|
}
|
|
|
|
static unsigned int
|
|
skip_past_newline_1 (string_type *ptr, unsigned int idx)
|
|
{
|
|
while (at (ptr, idx)
|
|
&& at (ptr, idx) != '\n')
|
|
idx++;
|
|
if (at (ptr, idx) == '\n')
|
|
return idx + 1;
|
|
return idx;
|
|
}
|
|
|
|
static void
|
|
check_range (void)
|
|
{
|
|
if (tos < stack)
|
|
die ("underflow in string stack");
|
|
if (tos >= stack + STACK)
|
|
die ("overflow in string stack");
|
|
}
|
|
|
|
static void
|
|
icheck_range (void)
|
|
{
|
|
if (isp < istack)
|
|
die ("underflow in integer stack");
|
|
if (isp >= istack + STACK)
|
|
die ("overflow in integer stack");
|
|
}
|
|
|
|
static void
|
|
drop (void)
|
|
{
|
|
tos--;
|
|
check_range ();
|
|
delete_string (tos + 1);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
idrop (void)
|
|
{
|
|
isp--;
|
|
icheck_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
exec (dict_type *word)
|
|
{
|
|
pc = word->code;
|
|
while (pc->f)
|
|
pc->f ();
|
|
}
|
|
|
|
static void
|
|
call (void)
|
|
{
|
|
pcu *oldpc = pc;
|
|
dict_type *e = pc[1].e;
|
|
exec (e);
|
|
pc = oldpc + 2;
|
|
}
|
|
|
|
static void
|
|
remchar (void)
|
|
{
|
|
if (tos->write_idx)
|
|
tos->write_idx--;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
strip_trailing_newlines (void)
|
|
{
|
|
while (tos->write_idx > 0
|
|
&& (isspace ((unsigned char) at (tos, tos->write_idx - 1))
|
|
|| at (tos, tos->write_idx - 1) == '\n'))
|
|
tos->write_idx--;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
push_number (void)
|
|
{
|
|
isp++;
|
|
icheck_range ();
|
|
pc++;
|
|
*isp = pc->l;
|
|
pc++;
|
|
}
|
|
|
|
/* This is a wrapper for push_number just so we can correctly free the
|
|
variable at the end. */
|
|
static void
|
|
push_variable (void)
|
|
{
|
|
push_number ();
|
|
}
|
|
|
|
static void
|
|
push_text (void)
|
|
{
|
|
tos++;
|
|
check_range ();
|
|
init_string (tos);
|
|
pc++;
|
|
cattext (tos, pc->s);
|
|
pc++;
|
|
}
|
|
|
|
/* This function removes everything not inside comments starting on
|
|
the first char of the line from the string, also when copying
|
|
comments, removes blank space and leading *'s.
|
|
Blank lines are turned into one blank line. */
|
|
|
|
static void
|
|
remove_noncomments (string_type *src, string_type *dst)
|
|
{
|
|
unsigned int idx = 0;
|
|
|
|
while (at (src, idx))
|
|
{
|
|
/* Now see if we have a comment at the start of the line. */
|
|
if (at (src, idx) == '\n'
|
|
&& at (src, idx + 1) == '/'
|
|
&& at (src, idx + 2) == '*')
|
|
{
|
|
idx += 3;
|
|
|
|
idx = skip_white_and_stars (src, idx);
|
|
|
|
/* Remove leading dot */
|
|
if (at (src, idx) == '.')
|
|
idx++;
|
|
|
|
/* Copy to the end of the line, or till the end of the
|
|
comment. */
|
|
while (at (src, idx))
|
|
{
|
|
if (at (src, idx) == '\n')
|
|
{
|
|
/* end of line, echo and scrape of leading blanks */
|
|
if (at (src, idx + 1) == '\n')
|
|
catchar (dst, '\n');
|
|
catchar (dst, '\n');
|
|
idx++;
|
|
idx = skip_white_and_stars (src, idx);
|
|
}
|
|
else if (at (src, idx) == '*' && at (src, idx + 1) == '/')
|
|
{
|
|
idx += 2;
|
|
cattext (dst, "\nENDDD\n");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
catchar (dst, at (src, idx));
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_stack_level (void)
|
|
{
|
|
fprintf (stderr, "current string stack depth = %ld, ",
|
|
(long) (tos - stack));
|
|
fprintf (stderr, "current integer stack depth = %ld\n",
|
|
(long) (isp - istack));
|
|
pc++;
|
|
}
|
|
|
|
/* turn {*
|
|
and *} into comments */
|
|
|
|
static void
|
|
translatecomments (void)
|
|
{
|
|
unsigned int idx = 0;
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
while (at (tos, idx))
|
|
{
|
|
if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
|
|
{
|
|
cattext (&out, "/*");
|
|
idx += 2;
|
|
}
|
|
else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
|
|
{
|
|
cattext (&out, "*/");
|
|
idx += 2;
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, at (tos, idx));
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
overwrite_string (tos, &out);
|
|
|
|
pc++;
|
|
}
|
|
|
|
/* Wrap tos-1 as a C comment, indenting by tos. */
|
|
|
|
static void
|
|
wrap_comment (void)
|
|
{
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
catstr (&out, tos);
|
|
cattext (&out, "/* ");
|
|
for (unsigned int idx = 0; at (tos - 1, idx); idx++)
|
|
{
|
|
catchar (&out, at (tos - 1, idx));
|
|
if (at (tos - 1, idx) == '\n' && at (tos - 1, idx + 1) != '\n')
|
|
{
|
|
catstr (&out, tos);
|
|
cattext (&out, " ");
|
|
}
|
|
}
|
|
cattext (&out, " */");
|
|
|
|
overwrite_string (tos - 1, &out);
|
|
drop ();
|
|
|
|
pc++;
|
|
}
|
|
|
|
/* Mod tos so that only lines with leading dots remain */
|
|
static void
|
|
outputdots (void)
|
|
{
|
|
unsigned int idx = 0;
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
while (at (tos, idx))
|
|
{
|
|
/* Every iteration begins at the start of a line. */
|
|
if (at (tos, idx) == '.')
|
|
{
|
|
char c;
|
|
int spaces;
|
|
|
|
idx++;
|
|
spaces = 0;
|
|
while ((c = at (tos, idx)) && c != '\n')
|
|
{
|
|
if (spaces >= 0)
|
|
{
|
|
if (c == ' ')
|
|
{
|
|
spaces++;
|
|
idx++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
while (spaces >= 8)
|
|
{
|
|
catchar (&out, '\t');
|
|
spaces -= 8;
|
|
}
|
|
while (spaces-- > 0)
|
|
catchar (&out, ' ');
|
|
}
|
|
}
|
|
if (c == '{' && at (tos, idx + 1) == '*')
|
|
{
|
|
cattext (&out, "/*");
|
|
idx += 2;
|
|
}
|
|
else if (c == '*' && at (tos, idx + 1) == '}')
|
|
{
|
|
cattext (&out, "*/");
|
|
idx += 2;
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, c);
|
|
idx++;
|
|
}
|
|
}
|
|
if (c == '\n')
|
|
idx++;
|
|
catchar (&out, '\n');
|
|
}
|
|
else
|
|
{
|
|
idx = skip_past_newline_1 (tos, idx);
|
|
}
|
|
}
|
|
|
|
overwrite_string (tos, &out);
|
|
pc++;
|
|
}
|
|
|
|
/* Find lines starting with . and | and put example around them on tos */
|
|
static void
|
|
courierize (void)
|
|
{
|
|
string_type out;
|
|
unsigned int idx = 0;
|
|
int command = 0;
|
|
|
|
init_string (&out);
|
|
|
|
while (at (tos, idx))
|
|
{
|
|
if (at (tos, idx) == '\n'
|
|
&& (at (tos, idx +1 ) == '.'
|
|
|| at (tos, idx + 1) == '|'))
|
|
{
|
|
cattext (&out, "\n@example\n");
|
|
do
|
|
{
|
|
idx += 2;
|
|
|
|
while (at (tos, idx) && at (tos, idx) != '\n')
|
|
{
|
|
if (command > 1)
|
|
{
|
|
/* We are inside {} parameters of some command;
|
|
Just pass through until matching brace. */
|
|
if (at (tos, idx) == '{')
|
|
++command;
|
|
else if (at (tos, idx) == '}')
|
|
--command;
|
|
}
|
|
else if (command != 0)
|
|
{
|
|
if (at (tos, idx) == '{')
|
|
++command;
|
|
else if (!islower ((unsigned char) at (tos, idx)))
|
|
--command;
|
|
}
|
|
else if (at (tos, idx) == '@'
|
|
&& islower ((unsigned char) at (tos, idx + 1)))
|
|
{
|
|
++command;
|
|
}
|
|
else if (at (tos, idx) == '{' && at (tos, idx + 1) == '*')
|
|
{
|
|
cattext (&out, "/*");
|
|
idx += 2;
|
|
continue;
|
|
}
|
|
else if (at (tos, idx) == '*' && at (tos, idx + 1) == '}')
|
|
{
|
|
cattext (&out, "*/");
|
|
idx += 2;
|
|
continue;
|
|
}
|
|
else if (at (tos, idx) == '{'
|
|
|| at (tos, idx) == '}')
|
|
{
|
|
catchar (&out, '@');
|
|
}
|
|
|
|
catchar (&out, at (tos, idx));
|
|
idx++;
|
|
}
|
|
catchar (&out, '\n');
|
|
}
|
|
while (at (tos, idx) == '\n'
|
|
&& ((at (tos, idx + 1) == '.')
|
|
|| (at (tos, idx + 1) == '|')))
|
|
;
|
|
cattext (&out, "@end example");
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, at (tos, idx));
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
overwrite_string (tos, &out);
|
|
pc++;
|
|
}
|
|
|
|
/* Finds any lines starting with "o ", if there are any, then turns
|
|
on @itemize @bullet, and @items each of them. Then ends with @end
|
|
itemize, inplace at TOS*/
|
|
|
|
static void
|
|
bulletize (void)
|
|
{
|
|
unsigned int idx = 0;
|
|
int on = 0;
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
while (at (tos, idx))
|
|
{
|
|
if (at (tos, idx) == '@'
|
|
&& at (tos, idx + 1) == '*')
|
|
{
|
|
cattext (&out, "*");
|
|
idx += 2;
|
|
}
|
|
else if (at (tos, idx) == '\n'
|
|
&& at (tos, idx + 1) == 'o'
|
|
&& isspace ((unsigned char) at (tos, idx + 2)))
|
|
{
|
|
if (!on)
|
|
{
|
|
cattext (&out, "\n@itemize @bullet\n");
|
|
on = 1;
|
|
|
|
}
|
|
cattext (&out, "\n@item\n");
|
|
idx += 3;
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, at (tos, idx));
|
|
if (on && at (tos, idx) == '\n'
|
|
&& at (tos, idx + 1) == '\n'
|
|
&& at (tos, idx + 2) != 'o')
|
|
{
|
|
cattext (&out, "@end itemize");
|
|
on = 0;
|
|
}
|
|
idx++;
|
|
|
|
}
|
|
}
|
|
if (on)
|
|
{
|
|
cattext (&out, "@end itemize\n");
|
|
}
|
|
|
|
delete_string (tos);
|
|
*tos = out;
|
|
pc++;
|
|
}
|
|
|
|
/* Turn <<foo>> into @code{foo} in place at TOS*/
|
|
|
|
static void
|
|
do_fancy_stuff (void)
|
|
{
|
|
unsigned int idx = 0;
|
|
string_type out;
|
|
init_string (&out);
|
|
while (at (tos, idx))
|
|
{
|
|
if (at (tos, idx) == '<'
|
|
&& at (tos, idx + 1) == '<'
|
|
&& !isspace ((unsigned char) at (tos, idx + 2)))
|
|
{
|
|
/* This qualifies as a << startup. */
|
|
idx += 2;
|
|
cattext (&out, "@code{");
|
|
while (at (tos, idx)
|
|
&& at (tos, idx) != '>' )
|
|
{
|
|
catchar (&out, at (tos, idx));
|
|
idx++;
|
|
|
|
}
|
|
cattext (&out, "}");
|
|
idx += 2;
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, at (tos, idx));
|
|
idx++;
|
|
}
|
|
}
|
|
delete_string (tos);
|
|
*tos = out;
|
|
pc++;
|
|
|
|
}
|
|
|
|
/* A command is all upper case,and alone on a line. */
|
|
|
|
static int
|
|
iscommand (string_type *ptr, unsigned int idx)
|
|
{
|
|
unsigned int len = 0;
|
|
while (at (ptr, idx))
|
|
{
|
|
if (isupper ((unsigned char) at (ptr, idx))
|
|
|| at (ptr, idx) == ' ' || at (ptr, idx) == '_')
|
|
{
|
|
len++;
|
|
idx++;
|
|
}
|
|
else if (at (ptr, idx) == '\n')
|
|
{
|
|
if (len > 3)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst)
|
|
{
|
|
int column = 0;
|
|
|
|
while (at (ptr, idx) && at (ptr, idx) != '\n')
|
|
{
|
|
if (at (ptr, idx) == '\t')
|
|
{
|
|
/* Expand tabs. Neither makeinfo nor TeX can cope well with
|
|
them. */
|
|
do
|
|
catchar (dst, ' ');
|
|
while (++column & 7);
|
|
}
|
|
else
|
|
{
|
|
catchar (dst, at (ptr, idx));
|
|
column++;
|
|
}
|
|
idx++;
|
|
|
|
}
|
|
catchar (dst, at (ptr, idx));
|
|
idx++;
|
|
return idx;
|
|
|
|
}
|
|
|
|
static void
|
|
icopy_past_newline (void)
|
|
{
|
|
tos++;
|
|
check_range ();
|
|
init_string (tos);
|
|
idx = copy_past_newline (ptr, idx, tos);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
kill_bogus_lines (void)
|
|
{
|
|
int sl;
|
|
|
|
int idx = 0;
|
|
int c;
|
|
int dot = 0;
|
|
|
|
string_type out;
|
|
init_string (&out);
|
|
/* Drop leading nl. */
|
|
while (at (tos, idx) == '\n')
|
|
{
|
|
idx++;
|
|
}
|
|
c = idx;
|
|
|
|
/* If the first char is a '.' prepend a newline so that it is
|
|
recognized properly later. */
|
|
if (at (tos, idx) == '.')
|
|
catchar (&out, '\n');
|
|
|
|
/* Find the last char. */
|
|
while (at (tos, idx))
|
|
{
|
|
idx++;
|
|
}
|
|
|
|
/* Find the last non white before the nl. */
|
|
idx--;
|
|
|
|
while (idx && isspace ((unsigned char) at (tos, idx)))
|
|
idx--;
|
|
idx++;
|
|
|
|
/* Copy buffer upto last char, but blank lines before and after
|
|
dots don't count. */
|
|
sl = 1;
|
|
|
|
while (c < idx)
|
|
{
|
|
if (at (tos, c) == '\n'
|
|
&& at (tos, c + 1) == '\n'
|
|
&& at (tos, c + 2) == '.')
|
|
{
|
|
/* Ignore two newlines before a dot. */
|
|
c++;
|
|
}
|
|
else if (at (tos, c) == '.' && sl)
|
|
{
|
|
/* remember that this line started with a dot. */
|
|
dot = 2;
|
|
}
|
|
else if (at (tos, c) == '\n'
|
|
&& at (tos, c + 1) == '\n'
|
|
&& dot)
|
|
{
|
|
c++;
|
|
/* Ignore two newlines when last line was dot. */
|
|
}
|
|
|
|
catchar (&out, at (tos, c));
|
|
if (at (tos, c) == '\n')
|
|
{
|
|
sl = 1;
|
|
|
|
if (dot == 2)
|
|
dot = 1;
|
|
else
|
|
dot = 0;
|
|
}
|
|
else
|
|
sl = 0;
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
/* Append nl. */
|
|
catchar (&out, '\n');
|
|
pc++;
|
|
delete_string (tos);
|
|
*tos = out;
|
|
|
|
}
|
|
|
|
static void
|
|
collapse_whitespace (void)
|
|
{
|
|
int last_was_ws = 0;
|
|
int idx;
|
|
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
for (idx = 0; at (tos, idx) != 0; ++idx)
|
|
{
|
|
char c = at (tos, idx);
|
|
if (isspace (c))
|
|
{
|
|
if (!last_was_ws)
|
|
{
|
|
catchar (&out, ' ');
|
|
last_was_ws = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
catchar (&out, c);
|
|
last_was_ws = 0;
|
|
}
|
|
}
|
|
|
|
pc++;
|
|
delete_string (tos);
|
|
*tos = out;
|
|
}
|
|
|
|
/* indent
|
|
Take the string at the top of the stack, do some prettying. */
|
|
|
|
static void
|
|
indent (void)
|
|
{
|
|
string_type out;
|
|
int tab = 0;
|
|
int idx = 0;
|
|
int ol = 0;
|
|
init_string (&out);
|
|
while (at (tos, idx))
|
|
{
|
|
switch (at (tos, idx))
|
|
{
|
|
case '\n':
|
|
catchar (&out, '\n');
|
|
idx++;
|
|
if (tab && at (tos, idx))
|
|
{
|
|
int i;
|
|
for (i = 0; i < tab - 1; i += 2)
|
|
catchar (&out, '\t');
|
|
if (i < tab)
|
|
cattext (&out, " ");
|
|
}
|
|
ol = 0;
|
|
break;
|
|
case '(':
|
|
if (ol == 0)
|
|
{
|
|
int i;
|
|
for (i = 1; i < tab - 1; i += 2)
|
|
catchar (&out, '\t');
|
|
if (i < tab)
|
|
cattext (&out, " ");
|
|
cattext (&out, " ");
|
|
}
|
|
tab++;
|
|
idx++;
|
|
catchar (&out, '(');
|
|
ol = 1;
|
|
break;
|
|
case ')':
|
|
tab--;
|
|
catchar (&out, ')');
|
|
idx++;
|
|
ol = 1;
|
|
break;
|
|
default:
|
|
catchar (&out, at (tos, idx));
|
|
ol = 1;
|
|
idx++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pc++;
|
|
delete_string (tos);
|
|
*tos = out;
|
|
|
|
}
|
|
|
|
static void
|
|
get_stuff_in_command (void)
|
|
{
|
|
tos++;
|
|
check_range ();
|
|
init_string (tos);
|
|
|
|
while (at (ptr, idx))
|
|
{
|
|
if (iscommand (ptr, idx))
|
|
break;
|
|
idx = copy_past_newline (ptr, idx, tos);
|
|
}
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
swap (void)
|
|
{
|
|
string_type t;
|
|
|
|
t = tos[0];
|
|
tos[0] = tos[-1];
|
|
tos[-1] = t;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
other_dup (void)
|
|
{
|
|
tos++;
|
|
check_range ();
|
|
init_string (tos);
|
|
catstr (tos, tos - 1);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
icatstr (void)
|
|
{
|
|
tos--;
|
|
check_range ();
|
|
catstr (tos, tos + 1);
|
|
delete_string (tos + 1);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
skip_past_newline (void)
|
|
{
|
|
idx = skip_past_newline_1 (ptr, idx);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
maybecatstr (void)
|
|
{
|
|
if (internal_wanted == *internal_mode)
|
|
{
|
|
catstr (tos - 1, tos);
|
|
}
|
|
delete_string (tos);
|
|
tos--;
|
|
check_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
catstrif (void)
|
|
{
|
|
int cond = isp[0];
|
|
isp--;
|
|
icheck_range ();
|
|
if (cond)
|
|
catstr (tos - 1, tos);
|
|
delete_string (tos);
|
|
tos--;
|
|
check_range ();
|
|
pc++;
|
|
}
|
|
|
|
char *
|
|
nextword (char *string, char **word)
|
|
{
|
|
char *word_start;
|
|
int idx;
|
|
char *dst;
|
|
char *src;
|
|
|
|
int length = 0;
|
|
|
|
while (isspace ((unsigned char) *string) || *string == '-')
|
|
{
|
|
if (*string == '-')
|
|
{
|
|
while (*string && *string != '\n')
|
|
string++;
|
|
|
|
}
|
|
else
|
|
{
|
|
string++;
|
|
}
|
|
}
|
|
if (!*string)
|
|
{
|
|
*word = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
word_start = string;
|
|
if (*string == '"')
|
|
{
|
|
do
|
|
{
|
|
string++;
|
|
length++;
|
|
if (*string == '\\')
|
|
{
|
|
string += 2;
|
|
length += 2;
|
|
}
|
|
}
|
|
while (*string != '"');
|
|
}
|
|
else
|
|
{
|
|
while (!isspace ((unsigned char) *string))
|
|
{
|
|
string++;
|
|
length++;
|
|
|
|
}
|
|
}
|
|
|
|
*word = xmalloc (length + 1);
|
|
|
|
dst = *word;
|
|
src = word_start;
|
|
|
|
for (idx = 0; idx < length; idx++)
|
|
{
|
|
if (src[idx] == '\\')
|
|
switch (src[idx + 1])
|
|
{
|
|
case 'n':
|
|
*dst++ = '\n';
|
|
idx++;
|
|
break;
|
|
case '"':
|
|
case '\\':
|
|
*dst++ = src[idx + 1];
|
|
idx++;
|
|
break;
|
|
default:
|
|
*dst++ = '\\';
|
|
break;
|
|
}
|
|
else
|
|
*dst++ = src[idx];
|
|
}
|
|
*dst++ = 0;
|
|
|
|
if (*string)
|
|
return string + 1;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
dict_type *
|
|
lookup_word (char *word)
|
|
{
|
|
dict_type *ptr = root;
|
|
while (ptr)
|
|
{
|
|
if (strcmp (ptr->word, word) == 0)
|
|
return ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
if (warning)
|
|
fprintf (stderr, "Can't find %s\n", word);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
free_words (void)
|
|
{
|
|
dict_type *ptr = root;
|
|
|
|
while (ptr)
|
|
{
|
|
dict_type *next;
|
|
|
|
free (ptr->word);
|
|
if (ptr->code)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ptr->code_end - 1; i ++)
|
|
if (ptr->code[i].f == push_text
|
|
&& ptr->code[i + 1].s)
|
|
{
|
|
free (ptr->code[i + 1].s - 1);
|
|
++i;
|
|
}
|
|
else if (ptr->code[i].f == push_variable)
|
|
{
|
|
free ((void *) ptr->code[i + 1].l);
|
|
++i;
|
|
}
|
|
free (ptr->code);
|
|
}
|
|
next = ptr->next;
|
|
free (ptr);
|
|
ptr = next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
perform (void)
|
|
{
|
|
tos = stack;
|
|
|
|
while (at (ptr, idx))
|
|
{
|
|
/* It's worth looking through the command list. */
|
|
if (iscommand (ptr, idx))
|
|
{
|
|
char *next;
|
|
dict_type *word;
|
|
|
|
(void) nextword (addr (ptr, idx), &next);
|
|
|
|
word = lookup_word (next);
|
|
|
|
if (word)
|
|
{
|
|
exec (word);
|
|
}
|
|
else
|
|
{
|
|
if (warning)
|
|
fprintf (stderr, "warning, %s is not recognised\n", next);
|
|
idx = skip_past_newline_1 (ptr, idx);
|
|
}
|
|
free (next);
|
|
}
|
|
else
|
|
idx = skip_past_newline_1 (ptr, idx);
|
|
}
|
|
}
|
|
|
|
dict_type *
|
|
newentry (char *word)
|
|
{
|
|
dict_type *new_d = xmalloc (sizeof (*new_d));
|
|
new_d->word = word;
|
|
new_d->next = root;
|
|
root = new_d;
|
|
new_d->code = xmalloc (sizeof (*new_d->code));
|
|
new_d->code_length = 1;
|
|
new_d->code_end = 0;
|
|
return new_d;
|
|
}
|
|
|
|
unsigned int
|
|
add_to_definition (dict_type *entry, pcu word)
|
|
{
|
|
if (entry->code_end == entry->code_length)
|
|
{
|
|
entry->code_length += 2;
|
|
entry->code = xrealloc (entry->code,
|
|
entry->code_length * sizeof (*entry->code));
|
|
}
|
|
entry->code[entry->code_end] = word;
|
|
|
|
return entry->code_end++;
|
|
}
|
|
|
|
void
|
|
add_intrinsic (char *name, void (*func) (void))
|
|
{
|
|
dict_type *new_d = newentry (xstrdup (name));
|
|
pcu p = { func };
|
|
add_to_definition (new_d, p);
|
|
p.f = 0;
|
|
add_to_definition (new_d, p);
|
|
}
|
|
|
|
static void
|
|
add_variable (char *name, intptr_t *loc)
|
|
{
|
|
dict_type *new_d = newentry (name);
|
|
pcu p = { push_variable };
|
|
add_to_definition (new_d, p);
|
|
p.l = (intptr_t) loc;
|
|
add_to_definition (new_d, p);
|
|
p.f = 0;
|
|
add_to_definition (new_d, p);
|
|
}
|
|
|
|
static void
|
|
add_intrinsic_variable (const char *name, intptr_t *loc)
|
|
{
|
|
add_variable (xstrdup (name), loc);
|
|
}
|
|
|
|
void
|
|
compile (char *string)
|
|
{
|
|
/* Add words to the dictionary. */
|
|
char *word;
|
|
|
|
string = nextword (string, &word);
|
|
while (string && *string && word[0])
|
|
{
|
|
if (word[0] == ':')
|
|
{
|
|
dict_type *ptr;
|
|
pcu p;
|
|
|
|
/* Compile a word and add to dictionary. */
|
|
free (word);
|
|
string = nextword (string, &word);
|
|
if (!string)
|
|
continue;
|
|
ptr = newentry (word);
|
|
string = nextword (string, &word);
|
|
if (!string)
|
|
{
|
|
free (ptr->code);
|
|
free (ptr);
|
|
continue;
|
|
}
|
|
|
|
while (word[0] != ';')
|
|
{
|
|
switch (word[0])
|
|
{
|
|
case '"':
|
|
/* got a string, embed magic push string
|
|
function */
|
|
p.f = push_text;
|
|
add_to_definition (ptr, p);
|
|
p.s = word + 1;
|
|
add_to_definition (ptr, p);
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
/* Got a number, embedd the magic push number
|
|
function */
|
|
p.f = push_number;
|
|
add_to_definition (ptr, p);
|
|
p.l = atol (word);
|
|
add_to_definition (ptr, p);
|
|
free (word);
|
|
break;
|
|
default:
|
|
p.f = call;
|
|
add_to_definition (ptr, p);
|
|
p.e = lookup_word (word);
|
|
add_to_definition (ptr, p);
|
|
free (word);
|
|
}
|
|
|
|
string = nextword (string, &word);
|
|
}
|
|
p.f = 0;
|
|
add_to_definition (ptr, p);
|
|
free (word);
|
|
string = nextword (string, &word);
|
|
}
|
|
else if (strcmp (word, "variable") == 0)
|
|
{
|
|
free (word);
|
|
string = nextword (string, &word);
|
|
if (!string)
|
|
continue;
|
|
intptr_t *loc = xmalloc (sizeof (intptr_t));
|
|
*loc = 0;
|
|
add_variable (word, loc);
|
|
string = nextword (string, &word);
|
|
}
|
|
else
|
|
{
|
|
fprintf (stderr, "syntax error at %s\n", string - 1);
|
|
}
|
|
}
|
|
free (word);
|
|
}
|
|
|
|
static void
|
|
bang (void)
|
|
{
|
|
*(intptr_t *) ((isp[0])) = isp[-1];
|
|
isp -= 2;
|
|
icheck_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
atsign (void)
|
|
{
|
|
isp[0] = *(intptr_t *) (isp[0]);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
hello (void)
|
|
{
|
|
printf ("hello\n");
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
stdout_ (void)
|
|
{
|
|
isp++;
|
|
icheck_range ();
|
|
*isp = 1;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
stderr_ (void)
|
|
{
|
|
isp++;
|
|
icheck_range ();
|
|
*isp = 2;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
print (void)
|
|
{
|
|
if (*isp == 1)
|
|
write_buffer (tos, stdout);
|
|
else if (*isp == 2)
|
|
write_buffer (tos, stderr);
|
|
else
|
|
fprintf (stderr, "print: illegal print destination `%" PRIdPTR "'\n", *isp);
|
|
isp--;
|
|
tos--;
|
|
icheck_range ();
|
|
check_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
read_in (string_type *str, FILE *file)
|
|
{
|
|
char buff[10000];
|
|
unsigned int r;
|
|
do
|
|
{
|
|
r = fread (buff, 1, sizeof (buff), file);
|
|
catbuf (str, buff, r);
|
|
}
|
|
while (r);
|
|
buff[0] = 0;
|
|
|
|
catbuf (str, buff, 1);
|
|
}
|
|
|
|
static void
|
|
usage (void)
|
|
{
|
|
fprintf (stderr, "usage: -[d|i|g] <file >file\n");
|
|
exit (33);
|
|
}
|
|
|
|
/* There is no reliable way to declare exit. Sometimes it returns
|
|
int, and sometimes it returns void. Sometimes it changes between
|
|
OS releases. Trying to get it declared correctly in the hosts file
|
|
is a pointless waste of time. */
|
|
|
|
static void
|
|
chew_exit (void)
|
|
{
|
|
exit (0);
|
|
}
|
|
|
|
int
|
|
main (int ac, char *av[])
|
|
{
|
|
unsigned int i;
|
|
string_type buffer;
|
|
string_type pptr;
|
|
|
|
init_string (&buffer);
|
|
init_string (&pptr);
|
|
init_string (stack + 0);
|
|
tos = stack + 1;
|
|
ptr = &pptr;
|
|
|
|
add_intrinsic ("push_text", push_text);
|
|
add_intrinsic ("!", bang);
|
|
add_intrinsic ("@", atsign);
|
|
add_intrinsic ("hello", hello);
|
|
add_intrinsic ("stdout", stdout_);
|
|
add_intrinsic ("stderr", stderr_);
|
|
add_intrinsic ("print", print);
|
|
add_intrinsic ("skip_past_newline", skip_past_newline);
|
|
add_intrinsic ("catstr", icatstr);
|
|
add_intrinsic ("copy_past_newline", icopy_past_newline);
|
|
add_intrinsic ("dup", other_dup);
|
|
add_intrinsic ("drop", drop);
|
|
add_intrinsic ("idrop", idrop);
|
|
add_intrinsic ("remchar", remchar);
|
|
add_intrinsic ("get_stuff_in_command", get_stuff_in_command);
|
|
add_intrinsic ("do_fancy_stuff", do_fancy_stuff);
|
|
add_intrinsic ("bulletize", bulletize);
|
|
add_intrinsic ("courierize", courierize);
|
|
/* If the following line gives an error, exit() is not declared in the
|
|
../hosts/foo.h file for this host. Fix it there, not here! */
|
|
/* No, don't fix it anywhere; see comment on chew_exit--Ian Taylor. */
|
|
add_intrinsic ("exit", chew_exit);
|
|
add_intrinsic ("swap", swap);
|
|
add_intrinsic ("outputdots", outputdots);
|
|
add_intrinsic ("maybecatstr", maybecatstr);
|
|
add_intrinsic ("catstrif", catstrif);
|
|
add_intrinsic ("translatecomments", translatecomments);
|
|
add_intrinsic ("wrap_comment", wrap_comment);
|
|
add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
|
|
add_intrinsic ("indent", indent);
|
|
add_intrinsic ("print_stack_level", print_stack_level);
|
|
add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
|
|
add_intrinsic ("collapse_whitespace", collapse_whitespace);
|
|
|
|
internal_mode = xmalloc (sizeof (intptr_t));
|
|
*internal_mode = 0;
|
|
add_intrinsic_variable ("internalmode", internal_mode);
|
|
|
|
/* Put a nl at the start. */
|
|
catchar (&buffer, '\n');
|
|
|
|
read_in (&buffer, stdin);
|
|
remove_noncomments (&buffer, ptr);
|
|
for (i = 1; i < (unsigned int) ac; i++)
|
|
{
|
|
if (av[i][0] == '-')
|
|
{
|
|
if (av[i][1] == 'f')
|
|
{
|
|
string_type b;
|
|
FILE *f;
|
|
init_string (&b);
|
|
|
|
f = fopen (av[i + 1], "r");
|
|
if (!f)
|
|
{
|
|
fprintf (stderr, "Can't open the input file %s\n",
|
|
av[i + 1]);
|
|
return 33;
|
|
}
|
|
|
|
read_in (&b, f);
|
|
compile (b.ptr);
|
|
perform ();
|
|
delete_string (&b);
|
|
}
|
|
else if (av[i][1] == 'i')
|
|
{
|
|
internal_wanted = 1;
|
|
}
|
|
else if (av[i][1] == 'w')
|
|
{
|
|
warning = 1;
|
|
}
|
|
else
|
|
usage ();
|
|
}
|
|
}
|
|
write_buffer (stack + 0, stdout);
|
|
free_words ();
|
|
delete_string (&pptr);
|
|
delete_string (&buffer);
|
|
if (tos != stack)
|
|
{
|
|
fprintf (stderr, "finishing with current stack level %ld\n",
|
|
(long) (tos - stack));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|