hl-vt100/src/lw_terminal_parser.c

229 lines
6.3 KiB
C

/*
* Copyright (c) 2016 Julien Palard.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#ifndef NDEBUG
# include <stdio.h>
#endif
#include "lw_terminal_parser.h"
static void lw_terminal_parser_push(struct lw_terminal *this, char c)
{
if (this->stack_ptr >= TERM_STACK_SIZE)
return ;
this->stack[this->stack_ptr++] = c;
}
static void lw_terminal_parser_parse_params(struct lw_terminal *this)
{
unsigned int i;
int got_something;
got_something = 0;
this->argc = 0;
this->argv[0] = 0;
for (i = 0; i < this->stack_ptr; ++i)
{
if (this->stack[i] >= '0' && this->stack[i] <= '9')
{
got_something = 1;
this->argv[this->argc] = this->argv[this->argc] * 10
+ this->stack[i] - '0';
}
else if (this->stack[i] == ';')
{
got_something = 0;
this->argc += 1;
this->argv[this->argc] = 0;
}
}
this->argc += got_something;
}
static void lw_terminal_parser_call_CSI(struct lw_terminal *this, char c)
{
lw_terminal_parser_parse_params(this);
if (((term_action *)&this->callbacks.csi)[c - '0'] == NULL)
{
if (this->unimplemented != NULL)
this->unimplemented(this, "CSI", c);
goto leave;
}
((term_action *)&this->callbacks.csi)[c - '0'](this);
leave:
this->state = INIT;
this->flag = '\0';
this->stack_ptr = 0;
this->argc = 0;
}
static void lw_terminal_parser_call_ESC(struct lw_terminal *this, char c)
{
if (((term_action *)&this->callbacks.esc)[c - '0'] == NULL)
{
if (this->unimplemented != NULL)
this->unimplemented(this, "ESC", c);
goto leave;
}
((term_action *)&this->callbacks.esc)[c - '0'](this);
leave:
this->state = INIT;
this->stack_ptr = 0;
this->argc = 0;
}
static void lw_terminal_parser_call_HASH(struct lw_terminal *this, char c)
{
if (((term_action *)&this->callbacks.hash)[c - '0'] == NULL)
{
if (this->unimplemented != NULL)
this->unimplemented(this, "HASH", c);
goto leave;
}
((term_action *)&this->callbacks.hash)[c - '0'](this);
leave:
this->state = INIT;
this->stack_ptr = 0;
this->argc = 0;
}
static void lw_terminal_parser_call_GSET(struct lw_terminal *this, char c)
{
if (c < '0' || c > 'B'
|| ((term_action *)&this->callbacks.scs)[c - '0'] == NULL)
{
if (this->unimplemented != NULL)
this->unimplemented(this, "GSET", c);
goto leave;
}
((term_action *)&this->callbacks.scs)[c - '0'](this);
leave:
this->state = INIT;
this->stack_ptr = 0;
this->argc = 0;
}
/*
** INIT
** \_ ESC "\033"
** | \_ CSI "\033["
** | | \_ c == '?' : term->flag = '?'
** | | \_ c == ';' || (c >= '0' && c <= '9') : term_push
** | | \_ else : term_call_CSI()
** | \_ HASH "\033#"
** | | \_ term_call_hash()
** | \_ G0SET "\033("
** | | \_ term_call_GSET()
** | \_ G1SET "\033)"
** | | \_ term_call_GSET()
** \_ term->write()
*/
void lw_terminal_parser_read(struct lw_terminal *this, char c)
{
if (this->state == INIT)
{
if (c == '\033')
this->state = ESC;
else
this->write(this, c);
}
else if (this->state == ESC)
{
if (c == '[')
this->state = CSI;
else if (c == '#')
this->state = HASH;
else if (c == '(')
this->state = G0SET;
else if (c == ')')
this->state = G1SET;
else if (c >= '0' && c <= 'z')
lw_terminal_parser_call_ESC(this, c);
else this->write(this, c);
}
else if (this->state == HASH)
{
if (c >= '0' && c <= '9')
lw_terminal_parser_call_HASH(this, c);
else
this->write(this, c);
}
else if (this->state == G0SET || this->state == G1SET)
{
lw_terminal_parser_call_GSET(this, c);
}
else if (this->state == CSI)
{
if (c == '?')
this->flag = '?';
else if (c == ';' || (c >= '0' && c <= '9'))
lw_terminal_parser_push(this, c);
else if (c >= '?' && c <= 'z')
lw_terminal_parser_call_CSI(this, c);
else
this->write(this, c);
}
}
void lw_terminal_parser_read_str(struct lw_terminal *this, char *c)
{
while (*c)
lw_terminal_parser_read(this, *c++);
}
#ifndef NDEBUG
void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr)
{
unsigned int argc;
fprintf(stderr, "WARNING: UNIMPLEMENTED %s (", seq);
for (argc = 0; argc < this->argc; ++argc)
{
fprintf(stderr, "%d", this->argv[argc]);
if (argc != this->argc - 1)
fprintf(stderr, ", ");
}
fprintf(stderr, ")%o\n", chr);
}
#else
void lw_terminal_parser_default_unimplemented(struct lw_terminal* this, char *seq, char chr)
{
this = this;
seq = seq;
chr = chr;
}
#endif
struct lw_terminal *lw_terminal_parser_init(void)
{
return calloc(1, sizeof(struct lw_terminal));
}
void lw_terminal_parser_destroy(struct lw_terminal* this)
{
free(this);
}