/*----------------------------------------------------------------------
* Module name: user
* Author name: Arkadiusz Firus
* Create date: 01 Jul 08
* Purpose: User's defined output module
*----------------------------------------------------------------------
* Changes:
* 21 Aug 10, daved@physiol.usyd.edu.au: test feof() rather than EOF for
AIX support
* 23 Sep 11, daved - provision for escaping backslash in config files
* 07 Oct 11, jf@dockes.org: major changes including adding GETC_OR_END
* macro and calling get_unicode_char instead of get_unicode_utf8
*--------------------------------------------------------------------*/
#ifndef HAVE_STDIO_H
#include <stdio.h>
#define HAVE_STDIO_H
#endif
#ifndef HAVE_ERRNO_H
#include <errno.h>
#define HAVE_ERRNO_H
#endif
#ifndef HAVE_STDLIB_H
#include <stdlib.h>
#define HAVE_STDLIB_H
#endif
#include "error.h"
#include "malloc.h"
#include "output.h"
#include <string.h>
#include "user.h"
#include "unicode.h"
#include "util.h"
typedef struct my_F
{
FILE *file;
int line_nr;
char *name;
} my_FILE;
/*========================================================================
* Name my_fclose
* Purpose: Opens file.
* Args: Path to file, mode in which file would be opened.
* Returns: Pointer to my_FILE
*=======================================================================*/
my_FILE *
my_fopen(char *file_name, char *mode)
{
my_FILE *f = (my_FILE *) malloc(sizeof(my_FILE));
if ((f->file = fopen(file_name, "r")) == NULL || (f->name = my_malloc((strlen(file_name) + 1) * sizeof(char))) == NULL)
return NULL;
f->line_nr = 1;
strcpy(f->name, file_name);
return f;
}
/*========================================================================
* Name my_fclose
* Purpose: Closes file and frees memory.
* Args: File to close.
* Returns: Nothing
*=======================================================================*/
void
my_fclose(my_FILE *f)
{
fclose(f->file);
my_free(f->name);
}
/*========================================================================
* Name give_definition
* Purpose: Reads definition value from a file.
* Args: File to read from.
* Returns: Definition or NULL on error.
*=======================================================================*/
#define ADD_CHAR(char)\
if (def_buffer_length == chars_nr)\
{\
if ((def = my_realloc(def, def_buffer_length, def_buffer_length * 2)) == NULL)\
{\
perror("Cannot allocate memory.");\
return NULL;\
}\
def_buffer_length *= 2;\
}\
def[chars_nr] = char;\
chars_nr++;
#define GETC_OR_END(F, C) { \
C = fgetc(F); \
if (feof(F) || ferror(F)) \
goto inputend; \
}
char *
give_definition(my_FILE *file)
{
char c, c2 = 0, c3 = 0, c4 = 0, *def, *unicode_char;
int i;
unsigned long def_buffer_length = STANDARD_BUFFER_LENGTH, chars_nr = 0;
if ((def = my_malloc(def_buffer_length)) == NULL)
return NULL;
GETC_OR_END(file->file, c);
while (c == '\t' || c == '#')
{
if (c == '#')
leave_line(file->file);
else
{
GETC_OR_END(file->file, c);
while (c != '\n')
{
if (c == 'U' && c2 == '<' && (c3 != '\\' || (c3 == '\\' && c4 == '\\')))
{
unicode_char = get_unicode_utf8(file->file);
for (i = 0; unicode_char[i] != '\0'; i++)
ADD_CHAR(unicode_char[i])
GETC_OR_END(file->file, c);
c2 = 0;
c3 = 0;
c4 = 0;
}
else
{
if (c2 == '<')
{
ADD_CHAR('<');
}
/* daved - 0.21.3 - allow escaping a backslash */
if (c == '\\' && c2 == '\\')
{
ADD_CHAR('\\');
c = 0;
c2 = 0;
c3 = 0;
}
else
/* daved - support \n in definitions */
if (c == 'n' && c2 == '\\')
{
ADD_CHAR('\n');
}
else
if ((c != '<' && c != '\\') || (c == '\\' && c2 == '\\'))
{
ADD_CHAR(c)
}
c4 = c3;
c3 = c2;
c2 = c;
GETC_OR_END(file->file, c);
}
}
file->line_nr++;
ADD_CHAR('\n');
}
GETC_OR_END(file->file, c);
}
inputend:
if (!feof(file->file) && !ferror(file->file))
{
ungetc(c, file->file);
}
if (chars_nr > 0)
def[chars_nr - 1] = '\0';
else
def[0] = '\0';
return def;
}
/*========================================================================
* Name: match_name
* Purpose: Tries to match known tag names with first argument
* Args: Tag name, Output Personality, file to read from
* Returns: -1 on error,
0 on success,
1 when tag name "name" is unknown
*=======================================================================*/
int
match_name (char *name, OutputPersonality *op, my_FILE *file)
{
struct definition
{
char *name;
char **variable;
} defs[] = DEFS_ARRAY(op);
char *endptr;
int i;
#if 1 /* daved 0.21.0-rc2 */
for (i = 0; defs[i].name && strcmp(defs[i].name, name); i++);
if (!defs[i].name)
#else
for (i = 0; defs[i].name[0] != '\0' && strcmp(defs[i].name, name); i++);
if (defs[i].name[0] == '\0')
#endif
{
i = strtol(name, &endptr, 10);
if (*endptr == '\0')
add_alias(op, i, give_definition(file));
else if (name[0] == '<' && name[1] == 'U')
add_alias(op, get_unicode(&name[2]), give_definition(file));
else
{
fprintf(stderr, "unrtf: unknown name \"%s\" in line %d of \"%s\"\n", name, file->line_nr, file->name);
return 1;
}
}
else
if ((*defs[i].variable = give_definition(file)) == NULL)
return -1;
return 0;
}
/*========================================================================
* Name: user_init
* Purpose: Generates user's defined output personality.
* Args: Path to file with definitions.
* Returns: OutputPersonality or NULL on error.
*=======================================================================*/
OutputPersonality *
user_init (OutputPersonality *op, char *definitions_file_path)
{
my_FILE *f;
char name_buffer[BUFFER_SIZE];
if (op == NULL)
op = op_create();
if ((f = my_fopen(definitions_file_path, "r")) == NULL)
{
perror(definitions_file_path);
return op;
}
while (fgets(name_buffer, BUFFER_SIZE - 1, f->file) != NULL
&& !feof(f->file) && !ferror(f->file))
{
if (name_buffer[strlen(name_buffer) - 1] != '\n')
leave_line(f->file);
f->line_nr++;
if (name_buffer[0] != '#' && name_buffer[0] != '\n')
{
name_buffer[strlen(name_buffer) - 1] = '\0';
if (match_name(name_buffer, op, f) == -1)
{
my_fclose(f);
free(f);
return NULL;
}
}
}
my_fclose(f);
free(f);
return op;
}