sh

/* $Id$ */
/* Copyright (c) 2004-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Unix sh */
/* 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, version 3 of the License.
*
* 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, see <http://www.gnu.org/licenses/>. */
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "scanner.h"
/* Scanner */
/* scanner_init */
static void _prompt(Scanner * scanner);
static int _next_file_prompt(Scanner * scanner);
static int _next_file(Scanner * scanner);
static int _next_string(Scanner * scanner);
void scanner_init(Scanner * scanner, Prefs * prefs, FILE * fp,
char const * string)
{
if((scanner->fp = fp) == NULL)
scanner->next = _next_string;
else if(*prefs & PREFS_i)
scanner->next = _next_file_prompt;
else
scanner->next = _next_file;
scanner->prompt = SP_PS1;
scanner->string = string;
}
static void _prompt(Scanner * scanner)
{
char * prompt;
char * env[SP_COUNT] = { "PS1", "PS2", "PS4" };
char * dflt[SP_COUNT] = { "$ ", "> ", "+ " };
if((prompt = getenv(env[scanner->prompt])) == NULL)
{
if(scanner->prompt == SP_PS1 && getuid() == 0)
prompt = "# ";
else
prompt = dflt[scanner->prompt];
}
fputs(prompt, stderr);
}
static void _lineno(void)
{
static unsigned int i = 1;
char lineno[11];
const int len = sizeof(lineno) - 1;
if(snprintf(lineno, len, "%u", i++) >= len)
lineno[len] = '\0';
if(setenv("LINENO", lineno, 1) != 0)
sh_error("setenv", 0);
}
static int _next_file_prompt(Scanner * scanner)
{
static int c = '\n';
if(c == '\n')
{
_prompt(scanner);
_lineno();
}
if((c = fgetc(scanner->fp)) == EOF)
fputc('\n', stderr);
return c;
}
static int _next_file(Scanner * scanner)
{
int c;
if((c = fgetc(scanner->fp)) == '\n')
_lineno();
return c;
}
static int _next_string(Scanner * scanner)
{
static unsigned int pos = 0;
if(scanner->string[pos] == '\0')
return EOF;
return scanner->string[pos++];
}
/* scanner_next */
static Token * _next_operator(Scanner * scanner, int * c);
static Token * _next_blank(Scanner * scanner, int * c);
static Token * _next_comment(Scanner * scanner, int * c);
static Token * _next_newline(int * c);
static Token * _next_word(Scanner * scanner, int * c);
Token * scanner_next(Scanner * scanner)
{
static int c = EOF;
Token * t;
#ifdef DEBUG
fprintf(stderr, "%s", "scanner_next()\n");
#endif
scanner->prompt = SP_PS1;
if(c == EOF && (c = scanner->next(scanner)) == EOF)
return token_new(TC_EOI, NULL);
scanner->prompt = SP_PS2;
_next_blank(scanner, &c);
_next_comment(scanner, &c);
if((t = _next_operator(scanner, &c)) != NULL
|| (t = _next_newline(&c)) != NULL
|| (t = _next_word(scanner, &c)) != NULL)
return t;
return NULL;
}
static Token * _next_operator(Scanner * scanner, int * c)
{
unsigned int i;
unsigned int pos = 0;
for(i = TC_OP_AND_IF; i <= TC_OP_GREAT;)
{
if(sTokenCode[i][pos] == '\0')
return token_new(i, NULL);
if(*c == sTokenCode[i][pos])
{
*c = scanner->next(scanner);
pos++;
}
else
i++;
}
return NULL;
}
static Token * _next_blank(Scanner * scanner, int * c)
{
while(*c == '\t' || *c == ' ')
*c = scanner->next(scanner);
return NULL;
}
static Token * _next_comment(Scanner * scanner, int * c)
{
if(*c != '#')
return NULL;
*c = scanner->next(scanner);
while(*c != EOF && *c != '\0' && *c != '\r' && *c != '\n')
*c = scanner->next(scanner);
return NULL;
}
static Token * _next_newline(int * c)
{
if(*c != '\r' && *c != '\n')
return NULL;
*c = EOF;
return token_new(TC_NEWLINE, NULL);
}
static char _word_escape(Scanner * scanner, int * c);
static char * _word_dquote(Scanner * scanner, int * c, char * str,
unsigned int * len);
static char * _word_quote(Scanner * scanner, int * c, char * str,
unsigned int * len);
static Token * _next_word(Scanner * scanner, int * c)
{
char eow[] = "\t \r\n&|;<>";
char * str = NULL;
unsigned int len = 0;
unsigned int i;
char * p;
for(; *c != EOF; *c = scanner->next(scanner))
{
for(i = 0; eow[i] != '\0' && *c != eow[i]; i++);
if(*c == eow[i])
break;
if(*c == '"')
p = _word_dquote(scanner, c, str, &len);
else if(*c == '\'')
p = _word_quote(scanner, c, str, &len);
else
{
if(*c == '\\' && (*c = _word_escape(scanner, c)) == EOF)
break;
if((p = realloc(str, len + 2)) != NULL)
{
str = p;
str[len++] = *c;
continue;
}
}
if(p == NULL)
{
sh_error("malloc", 0);
free(str);
return NULL;
}
str = p;
}
if(str == NULL)
return NULL;
str[len] = '\0';
/* FIXME aliases need to be replaced now; I suggest changing the
* scanner->next() function for a while but I don't really like it,
* moreover it wouldn't parse the alias into tokens... */
return token_new(TC_TOKEN, str);
}
static char _word_escape(Scanner * scanner, int * c)
{
char p;
if((p = scanner->next(scanner)) == EOF)
return *c;
return p;
}
static char const * _read_variable(Scanner * scanner, int * c);
static char * _word_dquote(Scanner * scanner, int * c, char * str,
unsigned int * len)
{
char * p;
char const * var;
if(str == NULL && (str = malloc(1)) == NULL)
return NULL;
for(*c = scanner->next(scanner); *c != EOF && *c != '"';
*c = scanner->next(scanner))
{
if(*c == '\\')
*c = _word_escape(scanner, c);
else if(*c == '$')
{
if((var = _read_variable(scanner, c)) == NULL)
return NULL;
if((p = realloc(str, (*len) + strlen(var) + 1)) == NULL)
return NULL;
str = p;
strcpy(&str[(*len)], var);
(*len) += strlen(var);
if(*c == '"')
break;
}
if((p = realloc(str, (*len) + 2)) == NULL)
return NULL;
str = p;
str[(*len)++] = *c;
}
return str;
}
static char const * _read_variable(Scanner * scanner, int * c)
{
char buf[80];
unsigned long i;
char * p;
char delim = '\0';
for(i = 0; i < sizeof(buf) - 1; i++)
{
*c = scanner->next(scanner);
if(i == 0 && *c == '{')
{
delim = '}';
i--;
continue;
}
if(i != 0 && delim != '\0' && *c == delim)
{
*c = scanner->next(scanner);
break;
}
if(*c == EOF || (!isalnum(*c) && *c != '_'))
break;
buf[i] = *c;
}
if(i == sizeof(buf) - 1)
return NULL;
if(i == 0)
return "$";
buf[i] = '\0';
if((p = getenv(buf)) == NULL)
return "";
return p;
}
static char * _word_quote(Scanner * scanner, int * c, char * str,
unsigned int * len)
{
char * p;
if(str == NULL && (str = malloc(1)) == NULL)
return NULL;
for(*c = scanner->next(scanner); *c != EOF && *c != '\'';
*c = scanner->next(scanner))
{
if((p = realloc(str, (*len) + 2)) == NULL)
return NULL;
str = p;
str[(*len)++] = *c;
}
return str;
}