master xplshn/aruu / cmd / posix / paste.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <stdlib.h>
  5#include <string.h>
  6
  7#include "utf.h"
  8#include "util.h"
  9
 10struct fdescr {
 11	FILE *fp;
 12	const char *name;
 13};
 14
 15static void
 16sequential(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
 17{
 18	Rune c, last;
 19	int i;
 20	size_t d;
 21
 22	for (i = 0; i < fdescrlen; i++) {
 23		d = 0;
 24		last = 0;
 25
 26		while (efgetrune(&c, dsc[i].fp, dsc[i].name)) {
 27			if (last == '\n') {
 28				if (delim[d] != '\0')
 29					efputrune(&delim[d], stdout, "<stdout>");
 30				d = (d + 1) % delimlen;
 31			}
 32
 33			if (c != '\n')
 34				efputrune(&c, stdout, "<stdout>");
 35			last = c;
 36		}
 37
 38		if (last == '\n')
 39			efputrune(&last, stdout, "<stdout>");
 40	}
 41}
 42
 43static void
 44parallel(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
 45{
 46	Rune c, d;
 47	int i, m, last;
 48
 49nextline:
 50	last = -1;
 51
 52	for (i = 0; i < fdescrlen; i++) {
 53		d = delim[(size_t)i % delimlen];
 54		c = 0;
 55
 56		while (efgetrune(&c, dsc[i].fp, dsc[i].name)) {
 57			for (m = last + 1; m < i; m++) {
 58				if (delim[(size_t)m % delimlen] != '\0')
 59					efputrune(&delim[(size_t)m % delimlen], stdout, "<stdout>");
 60			}
 61			last = i;
 62			if (c == '\n') {
 63				if (i != fdescrlen - 1)
 64					c = d;
 65				efputrune(&c, stdout, "<stdout>");
 66				break;
 67			}
 68			efputrune(&c, stdout, "<stdout>");
 69		}
 70
 71		if (c == 0 && last != -1) {
 72			if (i == fdescrlen - 1)
 73				putchar('\n');
 74			else if (d != '\0')
 75				efputrune(&d, stdout, "<stdout>");
 76			last++;
 77		}
 78	}
 79	if (last != -1)
 80		goto nextline;
 81}
 82
 83static void
 84usage(void)
 85{
 86	eprintf("usage: %s [-s] [-d list] file ...\n", argv0);
 87}
 88
 89// ?man paste: merge lines of files
 90// ?man arguments: file ...
 91// ?man merge corresponding lines of files side by side
 92int
 93main(int argc, char *argv[])
 94{
 95	struct fdescr *dsc;
 96	Rune *delim_rune = NULL;
 97	size_t delim_runelen, delim_bytelen = 1;
 98	int seq = 0, ret = 0, i;
 99	char *delim = "\t";
100
101	ARGBEGIN {
102	// ?man -s: silent mode or print summary
103	case 's':
104		seq = 1;
105		break;
106	// ?man -d:str: specify directory
107	case 'd':
108		delim = EARGF(usage());
109		delim_bytelen = unescape(delim);
110		break;
111	default:
112		usage();
113	} ARGEND
114
115	if (!argc)
116		usage();
117
118	/* populate delimiters */
119	delim_rune = ereallocarray(NULL,
120		utfmemlen(delim, delim_bytelen) + 1, sizeof(*delim_rune));
121	if (!(delim_runelen = utfntorunestr(delim, delim_bytelen, delim_rune))) {
122		usage();
123	}
124
125	/* populate file list */
126	dsc = ereallocarray(NULL, argc, sizeof(*dsc));
127
128	for (i = 0; i < argc; i++) {
129		if (!strcmp(argv[i], "-")) {
130			argv[i] = "<stdin>";
131			dsc[i].fp = stdin;
132		} else if (!(dsc[i].fp = fopen(argv[i], "r"))) {
133			eprintf("fopen %s:", argv[i]);
134		}
135		dsc[i].name = argv[i];
136	}
137
138	if (seq) {
139		sequential(dsc, argc, delim_rune, delim_runelen);
140	} else {
141		parallel(dsc, argc, delim_rune, delim_runelen);
142	}
143
144	for (i = 0; i < argc; i++)
145		if (dsc[i].fp != stdin && fshut(dsc[i].fp, argv[i]))
146			ret |= fshut(dsc[i].fp, argv[i]);
147
148	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
149
150	return ret;
151}