Files
bash/mksyntax.c
2024-12-17 09:51:38 -05:00

356 lines
6.4 KiB
C

/*
* mksyntax.c - construct shell syntax table for fast char attribute lookup.
*/
/* Copyright (C) 2000-2009,2012,2022-2024 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash 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.
Bash 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 Bash. If not, see <http://www.gnu.org/licenses/>.
*/
/* assume C90/POSIX-1992 compilation environment if cross-compiling */
#ifndef CROSS_COMPILING
# include <config.h>
#else
# include <buildconf.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include "chartypes.h"
#include "syntax.h"
#ifndef errno
extern int errno;
#endif
struct wordflag {
int flag;
char *fstr;
} wordflags[] = {
{ CWORD, "CWORD" },
{ CSHMETA, "CSHMETA" },
{ CSHBRK, "CSHBRK" },
{ CBACKQ, "CBACKQ" },
{ CQUOTE, "CQUOTE" },
{ CSPECL, "CSPECL" },
{ CEXP, "CEXP" },
{ CBSDQUOTE, "CBSDQUOTE" },
{ CBSHDOC, "CBSHDOC" },
{ CGLOB, "CGLOB" },
{ CXGLOB, "CXGLOB" },
{ CXQUOTE, "CXQUOTE" },
{ CSPECVAR, "CSPECVAR" },
{ CSUBSTOP, "CSUBSTOP" },
{ CBLANK, "CBLANK" },
};
#define N_WFLAGS (sizeof (wordflags) / sizeof (wordflags[0]))
#define SYNSIZE 256
int lsyntax[SYNSIZE];
int debug;
char *progname;
char preamble[] = "\
/*\n\
* This file was generated by mksyntax. DO NOT EDIT.\n\
*/\n\
\n";
char includes[] = "\
#include \"config.h\"\n\
#include \"stdc.h\"\n\
#include \"syntax.h\"\n\n";
static void
usage(void)
{
fprintf (stderr, "%s: usage: %s [-d] [-o filename]\n", progname, progname);
exit (2);
}
#ifdef INCLUDE_UNUSED
static int
getcflag (char *s)
{
int i;
for (i = 0; i < N_WFLAGS; i++)
if (strcmp (s, wordflags[i].fstr) == 0)
return wordflags[i].flag;
return -1;
}
#endif
static char *
cdesc (int i)
{
static char xbuf[16];
if (i == ' ')
return "SPC";
else if (ISPRINT (i))
{
xbuf[0] = i;
xbuf[1] = '\0';
return (xbuf);
}
else if (i == CTLESC)
return "CTLESC";
else if (i == CTLNUL)
return "CTLNUL";
else if (i == '\033') /* ASCII */
return "ESC";
xbuf[0] = '\\';
xbuf[2] = '\0';
switch (i)
{
case '\a': xbuf[1] = 'a'; break;
case '\v': xbuf[1] = 'v'; break;
case '\b': xbuf[1] = 'b'; break;
case '\f': xbuf[1] = 'f'; break;
case '\n': xbuf[1] = 'n'; break;
case '\r': xbuf[1] = 'r'; break;
case '\t': xbuf[1] = 't'; break;
default: sprintf (xbuf, "%d", i); break;
}
return xbuf;
}
static char *
getcstr (int f)
{
int i;
for (i = 0; i < N_WFLAGS; i++)
if (f == wordflags[i].flag)
return (wordflags[i].fstr);
return ((char *)NULL);
}
static void
addcstr (char *str, int flag)
{
char *s, *fstr;
unsigned char uc;
for (s = str; s && *s; s++)
{
uc = *s;
if (debug)
{
fstr = getcstr (flag);
fprintf(stderr, "added %s for character %s\n", fstr, cdesc(uc));
}
lsyntax[uc] |= flag;
}
}
static void
addcchar (unsigned char c, int flag)
{
char *fstr;
if (debug)
{
fstr = getcstr (flag);
fprintf (stderr, "added %s for character %s\n", fstr, cdesc(c));
}
lsyntax[c] |= flag;
}
static void
addblanks (void)
{
register int i;
unsigned char uc;
for (i = 0; i < SYNSIZE; i++)
{
uc = i;
/* Since we don't call setlocale(), this defaults to the "C" locale, and
the default blank characters will be space and tab. */
if (isblank (uc))
lsyntax[uc] |= CBLANK;
}
}
/* load up the correct flag values in lsyntax */
static void
load_lsyntax (void)
{
/* shell metacharacters */
addcstr (shell_meta_chars, CSHMETA);
/* shell word break characters */
addcstr (shell_break_chars, CSHBRK);
addcchar ('`', CBACKQ);
addcstr (shell_quote_chars, CQUOTE);
addcchar (CTLESC, CSPECL);
addcchar (CTLNUL, CSPECL);
addcstr (shell_exp_chars, CEXP);
addcstr (slashify_in_quotes, CBSDQUOTE);
addcstr (slashify_in_here_document, CBSHDOC);
addcstr (shell_glob_chars, CGLOB);
#if defined (EXTENDED_GLOB)
addcstr (ext_glob_chars, CXGLOB);
#endif
addcstr (shell_quote_chars, CXQUOTE);
addcchar ('\\', CXQUOTE);
addcstr ("@*#?-$!", CSPECVAR); /* omits $0...$9 and $_ */
addcstr ("-=?+", CSUBSTOP); /* OP in ${paramOPword} */
addblanks ();
}
static void
dump_lflags (FILE *fp, int ind)
{
int xflags, first, i;
xflags = lsyntax[ind];
first = 1;
if (xflags == 0)
fputs (wordflags[0].fstr, fp);
else
{
for (i = 1; i < N_WFLAGS; i++)
if (xflags & wordflags[i].flag)
{
if (first)
first = 0;
else
putc ('|', fp);
fputs (wordflags[i].fstr, fp);
}
}
}
static void
wcomment (FILE *fp, int i)
{
fputs ("\t\t/* ", fp);
fprintf (fp, "%s", cdesc(i));
fputs (" */", fp);
}
static void
dump_lsyntax (FILE *fp)
{
int i;
fprintf (fp, "int sh_syntabsiz = %d;\n", SYNSIZE);
fprintf (fp, "int sh_syntaxtab[%d] = {\n", SYNSIZE);
for (i = 0; i < SYNSIZE; i++)
{
putc ('\t', fp);
dump_lflags (fp, i);
putc (',', fp);
wcomment (fp, i);
putc ('\n', fp);
}
fprintf (fp, "};\n");
}
int
main(int argc, char **argv)
{
int opt, i;
char *filename;
FILE *fp;
if ((progname = strrchr (argv[0], '/')) == 0)
progname = argv[0];
else
progname++;
filename = (char *)NULL;
debug = 0;
while ((opt = getopt (argc, argv, "do:")) != EOF)
{
switch (opt)
{
case 'd':
debug = 1;
break;
case 'o':
filename = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (filename)
{
fp = fopen (filename, "w");
if (fp == 0)
{
fprintf (stderr, "%s: %s: cannot open: %s\n", progname, filename, strerror(errno));
exit (1);
}
}
else
{
filename = "stdout";
fp = stdout;
}
for (i = 0; i < SYNSIZE; i++)
lsyntax[i] = CWORD;
load_lsyntax ();
fprintf (fp, "%s\n", preamble);
fprintf (fp, "%s\n", includes);
dump_lsyntax (fp);
if (fp != stdout)
fclose (fp);
exit (0);
}