summaryrefslogtreecommitdiff
path: root/io.c
blob: 2715e1b33bc876e1c10791336ad981777750101a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "more.h"

ssize_t more_getline(struct more_file *mf, size_t lineno)
{
	if (mf->nlines <= lineno && mf->nlines != 0) {
		fsetpos(mf->f, &(mf->lines[mf->nlines - 1]));
		getline(&(mf->buf), &(mf->nbuf), mf->f);
	}

	while (mf->nlines <= lineno) {
		mf->nlines++;
		mf->lines = realloc(mf->lines, mf->nlines * sizeof(*mf->lines));

		fgetpos(mf->f, &(mf->lines[mf->nlines - 1]));

		getline(&(mf->buf), &(mf->nbuf), mf->f);

		if (mf->backing != mf->f) {
			fgetpos(mf->backing, &(mf->lines[mf->nlines - 1]));
			fputs(mf->buf, mf->backing);
		}
	}

	fsetpos(mf->backing, &(mf->lines[lineno]));
	return getline(&(mf->buf), &(mf->nbuf), mf->backing);
}

struct more_file more_open(const char *path)
{
	struct more_file mf = {
		.f = stdin,
	};

	if (strcmp(path, "-")) {
		mf.f = fopen(path, "r");
		if (!mf.f) {
			fprintf(stderr, "more: %s: %s\n", path, strerror(errno));
			return mf;
		}
	}

	fpos_t pos;
	if (fgetpos(mf.f, &pos) != 0) {
		mf.backing = tmpfile();
	} else {
		mf.backing = mf.f;
	}

	return mf;
}

void more_close(struct more_file *mf)
{
	if (mf->backing != mf->f) {
		fclose(mf->backing);
	}

	if (mf->f != stdin) {
		fclose(mf->f);
	}

	free(mf->lines);
	free(mf->buf);
}