shithub: neatroff

ref: 156edb7214f62f225bc009bcbd2953f0e99b7d42
dir: /in.c/

View raw version
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xroff.h"

struct inbuf {
	char path[64];		/* for file buffers */
	FILE *fin;
	char *buf;		/* for string buffers */
	char **args;
	int pos;
	int len;
	int backed;
	int nl;			/* read \n, if the previous char was not */
	struct inbuf *prev;
};

static struct inbuf *buf;
static char files[NFILES][PATHLEN];
static int nfiles;
static int cfile;
static int in_last;

static char **args_init(char **args);
static void args_free(char **args);

static void in_new(void)
{
	struct inbuf *next = malloc(sizeof(*next));
	memset(next, 0, sizeof(*next));
	next->backed = -1;
	next->prev = buf;
	buf = next;
}

void in_push(char *s, char **args)
{
	int len = strlen(s);
	in_new();
	buf->buf = malloc(len + 1);
	buf->len = len;
	strcpy(buf->buf, s);
	buf->args = args ? args_init(args) : NULL;
}

void in_pushnl(char *s, char **args)
{
	in_push(s, args);
	buf->nl = 1;
}

void in_source(char *path)
{
	FILE *fin = path && path[0] ? fopen(path, "r") : stdin;
	if (fin) {
		in_new();
		buf->fin = fin;
		if (path)
			snprintf(buf->path, sizeof(buf->path) - 1, "%s", path);
	}
}

void in_queue(char *path)
{
	if (nfiles < NFILES)
		snprintf(files[nfiles++], PATHLEN - 1, "%s", path ? path : "");
}

static void in_pop(void)
{
	struct inbuf *old = buf;
	buf = buf->prev;
	if (old->args)
		args_free(old->args);
	if (old->fin && old->path[0])
		fclose(old->fin);
	free(old->buf);
	free(old);
}

static int in_nextfile(void)
{
	while (!buf && cfile < nfiles)
		in_source(files[cfile++]);
	return !buf;
}

static int in_read(void)
{
	int c;
	while (buf || !in_nextfile()) {
		if (buf->nl-- > 0 && in_last != '\n')
			return '\n';
		if (buf->buf && buf->pos < buf->len)
			break;
		if (!buf->buf && (c = getc(buf->fin)) >= 0)
			return c;
		in_pop();
	}
	if (!buf)
		return -1;
	/* replacing \\ with \ only for buffers inserted via in_push() */
	if (buf->buf[buf->pos] == '\\' && buf->buf[buf->pos + 1] == '\\')
		buf->pos++;
	return buf->buf[buf->pos++];
}

int in_next(void)
{
	int c;
	if (!buf && in_nextfile())
		return -1;
	c = buf->backed;
	if (c >= 0)
		buf->backed = -1;
	else
		c = in_read();
	in_last = c;
	return c;
}

void in_back(int c)
{
	if (buf)
		buf->backed = c;
}

char *in_arg(int i)
{
	struct inbuf *cur = buf;
	while (cur && !cur->args)
		cur = cur->prev;
	return cur && cur->args && cur->args[i - 1] ? cur->args[i - 1] : "";
}

int in_nargs(void)
{
	struct inbuf *cur = buf;
	int n = 0;
	while (cur && !cur->args)
		cur = cur->prev;
	while (cur && cur->args && cur->args[n])
		n++;
	return n;
}

char *in_filename(void)
{
	struct inbuf *cur = buf;
	while (cur && !cur->fin)
		cur = cur->prev;
	return cur && cur->path[0] ? cur->path : "-";
}

static char **args_init(char **args)
{
	char **out = malloc(NARGS * sizeof(*out));
	int i;
	for (i = 0; i < NARGS; i++) {
		out[i] = NULL;
		if (args[i]) {
			int len = strlen(args[i]) + 1;
			out[i] = malloc(len);
			memcpy(out[i], args[i], len);
		}
	}
	return out;
}

static void args_free(char **args)
{
	int i;
	for (i = 0; i < NARGS; i++)
		if (args[i])
			free(args[i]);
	free(args);
}