c17f5a58f8
[COMPILER] * BUG: Add the branch to the version number when it comes from a 'VERSION' file.
594 lines
9.8 KiB
C
594 lines
9.8 KiB
C
/***************************************************************************
|
|
|
|
gbc_form.c
|
|
|
|
(c) 2000-2017 Benoît Minisini <g4mba5@gmail.com>
|
|
|
|
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 2, 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.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __GBC_FORM_C
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "gb_common.h"
|
|
#include "gb_error.h"
|
|
#include "gb_limit.h"
|
|
#include "gb_file.h"
|
|
#include "gb_str.h"
|
|
#include "gbc_compile.h"
|
|
#include "gbc_chown.h"
|
|
#include "gbc_form.h"
|
|
|
|
/*#define DEBUG*/
|
|
|
|
static char *_source;
|
|
static const char *_current;
|
|
|
|
static FORM_PARENT form_parent[MAX_FORM_PARENT];
|
|
static int form_parent_level;
|
|
|
|
static bool _no_trim = FALSE;
|
|
|
|
static char **_target = NULL;
|
|
static bool _convert = FALSE;
|
|
|
|
|
|
void FORM_print_len(const char *buffer, int len)
|
|
{
|
|
if (COMP_verbose)
|
|
printf("%.*s", len, buffer);
|
|
|
|
BUFFER_add(_target, buffer, len);
|
|
}
|
|
|
|
|
|
void FORM_print(const char *buffer)
|
|
{
|
|
if (COMP_verbose)
|
|
printf("%s", buffer);
|
|
|
|
BUFFER_add(_target, buffer, strlen(buffer));
|
|
}
|
|
|
|
|
|
void FORM_print_char(char c)
|
|
{
|
|
if (COMP_verbose)
|
|
putchar(c);
|
|
|
|
BUFFER_add_char(_target, c);
|
|
}
|
|
|
|
|
|
void FORM_print_indent()
|
|
{
|
|
int i;
|
|
|
|
if (!_convert)
|
|
return;
|
|
|
|
for (i = 2; i < form_parent_level; i++)
|
|
BUFFER_add_char(_target, '\t');
|
|
}
|
|
|
|
static void print_fmt(const char *before, const char *word, int len, const char *after)
|
|
{
|
|
FORM_print(before);
|
|
FORM_print_len(word, len);
|
|
FORM_print(after);
|
|
}
|
|
|
|
|
|
static void print_var(const char *before, const char *word, int len, const char *after)
|
|
{
|
|
FORM_print(before);
|
|
if (!word)
|
|
FORM_print_len("Me", 2);
|
|
else
|
|
{
|
|
FORM_print_char('{');
|
|
FORM_print_len(word, len);
|
|
FORM_print_char('}');
|
|
}
|
|
FORM_print(after);
|
|
}
|
|
|
|
|
|
static bool read_line(const char **str, int *len)
|
|
{
|
|
const char *start, *end;
|
|
unsigned char car;
|
|
int l;
|
|
|
|
if (_no_trim)
|
|
{
|
|
car = *_current;
|
|
if (!car)
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
for(;;)
|
|
{
|
|
car = *_current;
|
|
if (!car)
|
|
return TRUE;
|
|
if (car > ' ')
|
|
break;
|
|
|
|
_current++;
|
|
}
|
|
}
|
|
|
|
start = _current;
|
|
for(;;)
|
|
{
|
|
car = *_current++;
|
|
if (car == '\n') // || car =='\r' || !car)
|
|
break;
|
|
//if (car > ' ')
|
|
// nospace = _current;
|
|
}
|
|
|
|
end = _current;
|
|
l = (int)(end - start);
|
|
|
|
while (l > 0 && (uchar)end[-1] <= ' ')
|
|
{
|
|
end--;
|
|
l--;
|
|
}
|
|
|
|
*str = start;
|
|
*len = l;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static char *get_word(const char **str, int *len)
|
|
{
|
|
char car;
|
|
const char *pos;
|
|
|
|
*len = 0;
|
|
|
|
for(;;)
|
|
{
|
|
car = **str;
|
|
if (car == '\n')
|
|
return NULL;
|
|
if (!isspace(car))
|
|
break;
|
|
(*str)++;
|
|
}
|
|
|
|
pos = *str;
|
|
|
|
for(;;)
|
|
{
|
|
car = **str;
|
|
if (isspace(car))
|
|
break;
|
|
(*str)++;
|
|
(*len)++;
|
|
}
|
|
|
|
return (char *)pos;
|
|
}
|
|
|
|
|
|
static void parent_enter(const char *str, int len)
|
|
{
|
|
if (form_parent_level >= MAX_FORM_PARENT)
|
|
THROW("&1: too many nested containers", JOB->form);
|
|
|
|
form_parent[form_parent_level].name = (char *)str;
|
|
form_parent[form_parent_level].len = len;
|
|
form_parent_level++;
|
|
}
|
|
|
|
|
|
static void parent_leave(void)
|
|
{
|
|
if (form_parent_level == 0)
|
|
THROW("&1: syntax error", JOB->form);
|
|
|
|
form_parent_level--;
|
|
}
|
|
|
|
|
|
static void parent_get(char **str, int *len, int add)
|
|
{
|
|
if (form_parent_level < add)
|
|
{
|
|
*str = NULL;
|
|
*len = 0;
|
|
}
|
|
else
|
|
{
|
|
FORM_PARENT *fp = &form_parent[form_parent_level - add];
|
|
*str = fp->name;
|
|
*len = fp->len;
|
|
}
|
|
}
|
|
|
|
|
|
static void get_container(char **str, int *len)
|
|
{
|
|
parent_get(str, len, 2);
|
|
}
|
|
|
|
static void get_current(char **str, int *len)
|
|
{
|
|
parent_get(str, len, 1);
|
|
}
|
|
|
|
static void save_action(bool delete)
|
|
{
|
|
FILE *file;
|
|
const char *path;
|
|
char *name;
|
|
const char *line;
|
|
int len;
|
|
|
|
path = FILE_cat(FILE_get_dir(COMP_project), ".action", NULL);
|
|
mkdir(path, 0777);
|
|
FILE_set_owner(path, COMP_project);
|
|
|
|
name = STR_copy(FILE_set_ext(FILE_get_name(JOB->form), "action"));
|
|
path = FILE_cat(FILE_get_dir(COMP_project), ".action", name, NULL);
|
|
|
|
if (delete)
|
|
{
|
|
if (FILE_exist(path))
|
|
{
|
|
if (COMP_verbose)
|
|
printf("Deleting action file %s\n", path);
|
|
|
|
FILE_unlink(path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (COMP_verbose)
|
|
printf("Writing action file %s\n", path);
|
|
|
|
file = fopen(path, "w");
|
|
if (!file)
|
|
THROW("Cannot create action file: &1", path);
|
|
|
|
fputs("# Gambas Action File 3.0\n", file);
|
|
|
|
_no_trim = TRUE;
|
|
while (!read_line(&line, &len))
|
|
{
|
|
fwrite(line, sizeof(char), len, file);
|
|
putc('\n', file);
|
|
}
|
|
_no_trim = FALSE;
|
|
|
|
if (fclose(file))
|
|
THROW("Cannot create action file: &1", path);
|
|
|
|
FILE_set_owner(path, COMP_project);
|
|
}
|
|
|
|
STR_free(name);
|
|
}
|
|
|
|
char *FORM_get_file_family(const char *file, const FORM_FAMILY **family)
|
|
{
|
|
char *form;
|
|
const FORM_FAMILY *p;
|
|
|
|
if (strcmp(FILE_get_ext(file), "class"))
|
|
return NULL;
|
|
|
|
p = COMP_form_families;
|
|
|
|
while (p->ext)
|
|
{
|
|
form = STR_copy(FILE_set_ext(file, p->ext));
|
|
if (FILE_exist(form))
|
|
break;
|
|
STR_free(form);
|
|
form = NULL;
|
|
p++;
|
|
}
|
|
|
|
if (form) *family= p;
|
|
return form;
|
|
}
|
|
|
|
|
|
void FORM_do(char *source, bool convert, bool ctrl_public)
|
|
{
|
|
const char *line;
|
|
char *word;
|
|
char *win = NULL;
|
|
char *twin = NULL;
|
|
|
|
int len, len_twin;
|
|
const char *pos_rewind;
|
|
bool virtual;
|
|
bool public;
|
|
bool action;
|
|
|
|
if (!convert && JOB->form == NULL)
|
|
return;
|
|
|
|
_source = source;
|
|
_current = _source;
|
|
_convert = convert;
|
|
|
|
/* version */
|
|
|
|
if (!convert)
|
|
{
|
|
if (read_line(&line, &len))
|
|
goto _ERROR;
|
|
|
|
if (strncasecmp(line, "# Gambas Form File 3.0", len))
|
|
THROW("Bad form file version");
|
|
}
|
|
|
|
pos_rewind = _current;
|
|
form_parent_level = 0;
|
|
|
|
//FORM_print("@SECTION\n");
|
|
|
|
while (!read_line(&line, &len))
|
|
{
|
|
if (len == 0 || *line == '#' || *line == '\'')
|
|
continue;
|
|
|
|
if (*line == '{')
|
|
{
|
|
form_parent_level++;
|
|
line++;
|
|
|
|
word = get_word(&line, &len);
|
|
if (word == NULL)
|
|
goto _ERROR;
|
|
|
|
if (win != NULL)
|
|
{
|
|
if (word[0] == '!')
|
|
{
|
|
word++;
|
|
len--;
|
|
public = TRUE;
|
|
}
|
|
else
|
|
public = FALSE;
|
|
|
|
if (ctrl_public || public)
|
|
FORM_print("Public");
|
|
else
|
|
FORM_print("Private");
|
|
|
|
//print_fmt(" {%.*s} ", len, word);
|
|
print_fmt(" {", word, len, "} ");
|
|
}
|
|
|
|
if (win == NULL)
|
|
{
|
|
win = word;
|
|
}
|
|
|
|
word = get_word(&line, &len);
|
|
if (word == NULL)
|
|
goto _ERROR;
|
|
|
|
if (twin != NULL)
|
|
{
|
|
if (*word == '#')
|
|
{
|
|
word++;
|
|
len--;
|
|
}
|
|
//print_fmt("AS %.*s\n", len, word);
|
|
print_fmt("As ", word, len, "\n");
|
|
}
|
|
|
|
if (twin == NULL)
|
|
{
|
|
twin = word;
|
|
len_twin = len;
|
|
|
|
//print_fmt("INHERITS %.*s\n\n", len_twin, twin);
|
|
if (!convert)
|
|
print_fmt("Inherits ", twin, len_twin, "\n\n");
|
|
}
|
|
}
|
|
else if (*line == '}')
|
|
{
|
|
form_parent_level--;
|
|
if (form_parent_level <= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!convert)
|
|
FORM_print("\nPrivate Sub {@load}()\n\n");
|
|
|
|
_current = pos_rewind;
|
|
form_parent_level = 0;
|
|
|
|
while (!read_line(&line, &len))
|
|
{
|
|
if (len == 0 || *line == '#' || *line == '\'')
|
|
continue;
|
|
|
|
if (*line == '{')
|
|
{
|
|
line++;
|
|
|
|
word = get_word(&line, &len);
|
|
if (word == NULL)
|
|
goto _ERROR;
|
|
|
|
if (form_parent_level == 0)
|
|
{
|
|
parent_enter(NULL, 0);
|
|
if (!convert) FORM_print("With Me\n");
|
|
}
|
|
else
|
|
{
|
|
//print_fmt(" {%.*s} = NEW ", len, word);
|
|
if (word[0] == '!')
|
|
{
|
|
word++;
|
|
len--;
|
|
}
|
|
|
|
if (convert)
|
|
FORM_print_char('\n');
|
|
|
|
parent_enter(word, len);
|
|
FORM_print_indent();
|
|
print_fmt("{", word, len, "} = New ");
|
|
|
|
word = get_word(&line, &len);
|
|
if (word == NULL)
|
|
goto _ERROR;
|
|
|
|
if (*word == '#')
|
|
{
|
|
virtual = TRUE;
|
|
word++;
|
|
len--;
|
|
}
|
|
else
|
|
virtual = FALSE;
|
|
|
|
//print_fmt("%.*s", len, word);
|
|
FORM_print_len(word, len);
|
|
|
|
if (!virtual)
|
|
{
|
|
get_container(&word, &len);
|
|
print_var("(", word, len, ")");
|
|
}
|
|
|
|
word = get_word(&line, &len);
|
|
if (word == NULL)
|
|
get_current(&word, &len);
|
|
|
|
//print_fmt(" AS \"%.*s\"\n", len, word);
|
|
print_fmt(" As \"", word, len, "\"\n");
|
|
|
|
FORM_print_indent();
|
|
get_current(&word, &len);
|
|
print_var("With ", word, len, "\n");
|
|
}
|
|
}
|
|
else if (*line == '}')
|
|
{
|
|
if (!convert || form_parent_level > 1)
|
|
{
|
|
FORM_print_indent();
|
|
FORM_print("End With\n");
|
|
}
|
|
parent_leave();
|
|
if (form_parent_level == 0)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/*get_current(&word, &len_word);*/
|
|
/*FORM_print(" %.*s", len_word, word);*/
|
|
FORM_print_indent();
|
|
FORM_print_len("\t.", 2);
|
|
FORM_print_len(line, len);
|
|
FORM_print("\n");
|
|
}
|
|
}
|
|
|
|
if (form_parent_level > 0)
|
|
goto _ERROR;
|
|
|
|
if (!convert)
|
|
{
|
|
FORM_print("\nEnd\n\n");
|
|
|
|
// Create or delete the action file if needed
|
|
|
|
action = FALSE;
|
|
|
|
while (!read_line(&line, &len))
|
|
{
|
|
if (!strncasecmp(line, "# Gambas Action File 3.0", len))
|
|
{
|
|
save_action(FALSE);
|
|
action = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!action)
|
|
save_action(TRUE);
|
|
}
|
|
|
|
return;
|
|
|
|
_ERROR:
|
|
|
|
if (!convert)
|
|
THROW("&1: syntax error in form file", JOB->form);
|
|
else
|
|
THROW("&1: syntax error in form data");
|
|
}
|
|
|
|
|
|
void FORM_set_target(char **target)
|
|
{
|
|
_target = target;
|
|
}
|
|
|
|
void FORM_convert(const char *path)
|
|
{
|
|
char *source;
|
|
char *target;
|
|
|
|
BUFFER_create(&source);
|
|
BUFFER_add(&source, "{ Me *\n", 7);
|
|
if (BUFFER_load_file(&source, path))
|
|
THROW("Cannot load file: &1", path);
|
|
BUFFER_add(&source, "}\n\n\0", 4);
|
|
|
|
BUFFER_create(&target);
|
|
|
|
FORM_set_target(&target);
|
|
FORM_do(source, TRUE, FALSE);
|
|
BUFFER_add_char(&target, '\0');
|
|
|
|
puts(target);
|
|
|
|
BUFFER_delete(&source);
|
|
BUFFER_delete(&target);
|
|
}
|