1567 lines
28 KiB
C
1567 lines
28 KiB
C
/* chew
|
|
Copyright (C) 1990-2022 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> ;
|
|
|
|
*/
|
|
|
|
/* 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
|
|
paramstuff - convert full declaration into "PARAMS" form if not already
|
|
maybecatstr - do catstr if internal_mode == internal_wanted, discard
|
|
value in any case
|
|
translatecomments - turn {* and *} into comment delimiters
|
|
kill_bogus_lines - get rid of extra newlines
|
|
indent
|
|
internalmode - pop from integer stack, set `internalmode' to that value
|
|
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
|
|
|
|
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>
|
|
|
|
#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;
|
|
long 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;
|
|
int 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 */
|
|
|
|
long istack[STACK];
|
|
long *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
|
|
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 ((isspace ((unsigned char) at (tos, tos->write_idx - 1))
|
|
|| at (tos, tos->write_idx - 1) == '\n')
|
|
&& tos->write_idx > 0)
|
|
tos->write_idx--;
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
push_number (void)
|
|
{
|
|
isp++;
|
|
icheck_range ();
|
|
pc++;
|
|
*isp = pc->l;
|
|
pc++;
|
|
}
|
|
|
|
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:
|
|
foobar name(stuff);
|
|
into:
|
|
foobar
|
|
name PARAMS ((stuff));
|
|
and a blank line.
|
|
*/
|
|
|
|
static void
|
|
paramstuff (void)
|
|
{
|
|
unsigned int openp;
|
|
unsigned int fname;
|
|
unsigned int idx;
|
|
unsigned int len;
|
|
string_type out;
|
|
init_string (&out);
|
|
|
|
#define NO_PARAMS 1
|
|
|
|
/* Make sure that it's not already param'd or proto'd. */
|
|
if (NO_PARAMS
|
|
|| find (tos, "PARAMS") || find (tos, "PROTO") || !find (tos, "("))
|
|
{
|
|
catstr (&out, tos);
|
|
}
|
|
else
|
|
{
|
|
/* Find the open paren. */
|
|
for (openp = 0; at (tos, openp) != '(' && at (tos, openp); openp++)
|
|
;
|
|
|
|
fname = openp;
|
|
/* Step back to the fname. */
|
|
fname--;
|
|
while (fname && isspace ((unsigned char) at (tos, fname)))
|
|
fname--;
|
|
while (fname
|
|
&& !isspace ((unsigned char) at (tos,fname))
|
|
&& at (tos,fname) != '*')
|
|
fname--;
|
|
|
|
fname++;
|
|
|
|
/* Output type, omitting trailing whitespace character(s), if
|
|
any. */
|
|
for (len = fname; 0 < len; len--)
|
|
{
|
|
if (!isspace ((unsigned char) at (tos, len - 1)))
|
|
break;
|
|
}
|
|
for (idx = 0; idx < len; idx++)
|
|
catchar (&out, at (tos, idx));
|
|
|
|
cattext (&out, "\n"); /* Insert a newline between type and fnname */
|
|
|
|
/* Output function name, omitting trailing whitespace
|
|
character(s), if any. */
|
|
for (len = openp; 0 < len; len--)
|
|
{
|
|
if (!isspace ((unsigned char) at (tos, len - 1)))
|
|
break;
|
|
}
|
|
for (idx = fname; idx < len; idx++)
|
|
catchar (&out, at (tos, idx));
|
|
|
|
cattext (&out, " PARAMS (");
|
|
|
|
for (idx = openp; at (tos, idx) && at (tos, idx) != ';'; idx++)
|
|
catchar (&out, at (tos, idx));
|
|
|
|
cattext (&out, ");\n\n");
|
|
}
|
|
overwrite_string (tos, &out);
|
|
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++;
|
|
}
|
|
|
|
/* 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;
|
|
|
|
idx++;
|
|
|
|
while ((c = at (tos, idx)) && c != '\n')
|
|
{
|
|
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++;
|
|
}
|
|
|
|
/* indent
|
|
Take the string at the top of the stack, do some prettying. */
|
|
|
|
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
|
|
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':
|
|
cattext (&out, "\n");
|
|
idx++;
|
|
if (tab && at (tos, idx))
|
|
{
|
|
cattext (&out, " ");
|
|
}
|
|
ol = 0;
|
|
break;
|
|
case '(':
|
|
tab++;
|
|
if (ol == 0)
|
|
cattext (&out, " ");
|
|
idx++;
|
|
cattext (&out, "(");
|
|
ol = 1;
|
|
break;
|
|
case ')':
|
|
tab--;
|
|
cattext (&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
|
|
drop (void)
|
|
{
|
|
tos--;
|
|
check_range ();
|
|
delete_string (tos + 1);
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
idrop (void)
|
|
{
|
|
isp--;
|
|
icheck_range ();
|
|
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
|
|
internalmode (void)
|
|
{
|
|
internal_mode = *(isp);
|
|
isp--;
|
|
icheck_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
maybecatstr (void)
|
|
{
|
|
if (internal_wanted == internal_mode)
|
|
{
|
|
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;
|
|
}
|
|
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);
|
|
}
|
|
|
|
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
|
|
{
|
|
fprintf (stderr, "syntax error at %s\n", string - 1);
|
|
}
|
|
}
|
|
free (word);
|
|
}
|
|
|
|
static void
|
|
bang (void)
|
|
{
|
|
*(long *) ((isp[0])) = isp[-1];
|
|
isp -= 2;
|
|
icheck_range ();
|
|
pc++;
|
|
}
|
|
|
|
static void
|
|
atsign (void)
|
|
{
|
|
isp[0] = *(long *) (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 `%ld'\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 ("paramstuff", paramstuff);
|
|
add_intrinsic ("maybecatstr", maybecatstr);
|
|
add_intrinsic ("translatecomments", translatecomments);
|
|
add_intrinsic ("kill_bogus_lines", kill_bogus_lines);
|
|
add_intrinsic ("indent", indent);
|
|
add_intrinsic ("internalmode", internalmode);
|
|
add_intrinsic ("print_stack_level", print_stack_level);
|
|
add_intrinsic ("strip_trailing_newlines", strip_trailing_newlines);
|
|
|
|
/* 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;
|
|
}
|