Codebase list unrtf / 738f9630-a84b-415f-a5d8-5c971de2d9ea/main src / user.c
738f9630-a84b-415f-a5d8-5c971de2d9ea/main

Tree @738f9630-a84b-415f-a5d8-5c971de2d9ea/main (Download .tar.gz)

user.c @738f9630-a84b-415f-a5d8-5c971de2d9ea/mainraw · history · blame

/*----------------------------------------------------------------------
 * 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 = NULL;
	char name_buffer[BUFFER_SIZE];
	int err = 1;

	if (op == NULL)
	{
		op = op_create();
	}

	if ((f = my_fopen(definitions_file_path, "r")) == NULL)
	{
		perror(definitions_file_path);
		goto out;
	}

	while (fgets(name_buffer, BUFFER_SIZE - 1, f->file) != NULL
	        && !feof(f->file) && !ferror(f->file))
	{
		int ll = strlen(name_buffer);
		if (ll <= 0 || name_buffer[ll - 1] != '\n')
		{
			/* Something is very wrong. There is no reason to continue
			   parsing a bad config file. Bail out */
			fprintf(stderr, "%s: bad line at %d\n", definitions_file_path,
					f->line_nr);
			goto out;
		}

		f->line_nr++;

		if (name_buffer[0] != '#' && name_buffer[0] != '\n')
		{
			name_buffer[ll - 1] = '\0';

			if (match_name(name_buffer, op, f) == -1)
			{
				goto out;
			}
		}
	}

	err = 0;
out:
	if (f) {
		my_fclose(f);
		free(f);
	}

	return err ? NULL : op;
}