1139790Simp/*-
296888Smarcel * SPDX-License-Identifier: BSD-3-Clause
396888Smarcel *
496888Smarcel * Copyright (c) 1989, 1993, 1994
596888Smarcel *	The Regents of the University of California.  All rights reserved.
696888Smarcel *
796888Smarcel * This code is derived from software contributed to Berkeley by
896888Smarcel * Case Larsen.
996888Smarcel *
1096888Smarcel * Redistribution and use in source and binary forms, with or without
1196888Smarcel * modification, are permitted provided that the following conditions
1296888Smarcel * are met:
1396888Smarcel * 1. Redistributions of source code must retain the above copyright
1496888Smarcel *    notice, this list of conditions and the following disclaimer.
1596888Smarcel * 2. Redistributions in binary form must reproduce the above copyright
1696888Smarcel *    notice, this list of conditions and the following disclaimer in the
1796888Smarcel *    documentation and/or other materials provided with the distribution.
1896888Smarcel * 3. Neither the name of the University nor the names of its contributors
1996888Smarcel *    may be used to endorse or promote products derived from this software
2096888Smarcel *    without specific prior written permission.
2196888Smarcel *
2296888Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2396888Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2496888Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2596888Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2696888Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2796888Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2896888Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29115705Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30115705Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31115705Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32103064Speter * SUCH DAMAGE.
33103064Speter */
3496888Smarcel
3596888Smarcel#include <err.h>
3696888Smarcel#include <limits.h>
3796888Smarcel#include <locale.h>
3896888Smarcel#include <stdint.h>
39102946Siedowse#include <stdio.h>
40103352Sbde#include <stdlib.h>
4196888Smarcel#include <string.h>
4296888Smarcel#include <unistd.h>
43103073Sbde#include <wchar.h>
4496888Smarcel#include <wctype.h>
4596888Smarcel
4696888Smarcelstatic int iflag;
4796888Smarcelstatic const char *tabs[] = { "", "\t", "\t\t" };
48246085Sjhb
4996888Smarcelstatic FILE	*file(const char *);
50147741Sdelphijstatic wchar_t	*convert(const char *);
51103064Speterstatic void	show(FILE *, const char *, const char *, char **, size_t *);
52103064Speterstatic void	usage(void);
53103064Speter
5496888Smarcelint
5596888Smarcelmain(int argc, char *argv[])
5696888Smarcel{
5796888Smarcel	int comp, read1, read2;
5896888Smarcel	int ch, flag1, flag2, flag3;
5996888Smarcel	FILE *fp1, *fp2;
6096888Smarcel	const char *col1, *col2, *col3;
6196888Smarcel	size_t line1len, line2len;
6296888Smarcel	char *line1, *line2;
6396888Smarcel	ssize_t n1, n2;
6496888Smarcel	wchar_t *tline1, *tline2;
6596888Smarcel	const char **p;
6696888Smarcel
6796888Smarcel	(void) setlocale(LC_ALL, "");
6896888Smarcel
6996888Smarcel	flag1 = flag2 = flag3 = 1;
7096888Smarcel
7196888Smarcel	while ((ch = getopt(argc, argv, "123i")) != -1)
72304188Sjhb		switch(ch) {
7396888Smarcel		case '1':
7496888Smarcel			flag1 = 0;
7596888Smarcel			break;
7696888Smarcel		case '2':
7796888Smarcel			flag2 = 0;
7896888Smarcel			break;
7996888Smarcel		case '3':
8096888Smarcel			flag3 = 0;
8196888Smarcel			break;
8296888Smarcel		case 'i':
8396888Smarcel			iflag = 1;
8496888Smarcel			break;
8596888Smarcel		case '?':
8696888Smarcel		default:
8796888Smarcel			usage();
8896888Smarcel		}
8996888Smarcel	argc -= optind;
90131575Sstefanf	argv += optind;
9196888Smarcel
9296888Smarcel	if (argc != 2)
9396888Smarcel		usage();
94293575Sdchagin
9596888Smarcel	fp1 = file(argv[0]);
9696888Smarcel	fp2 = file(argv[1]);
9796888Smarcel
9896888Smarcel	/* for each column printed, add another tab offset */
9996888Smarcel	p = tabs;
10096888Smarcel	col1 = col2 = col3 = NULL;
10196888Smarcel	if (flag1)
10296888Smarcel		col1 = *p++;
10396888Smarcel	if (flag2)
10496888Smarcel		col2 = *p++;
10596888Smarcel	if (flag3)
10696888Smarcel		col3 = *p;
10796888Smarcel
10896888Smarcel	line1len = line2len = 0;
10996888Smarcel	line1 = line2 = NULL;
11096888Smarcel	n1 = n2 = -1;
11196888Smarcel
11296888Smarcel	for (read1 = read2 = 1;;) {
11396888Smarcel		/* read next line, check for EOF */
11496888Smarcel		if (read1) {
11596888Smarcel			n1 = getline(&line1, &line1len, fp1);
11696888Smarcel			if (n1 < 0 && ferror(fp1))
11796888Smarcel				err(1, "%s", argv[0]);
11896888Smarcel			if (n1 > 0 && line1[n1 - 1] == '\n')
11996888Smarcel				line1[n1 - 1] = '\0';
12096888Smarcel
12196888Smarcel		}
12296888Smarcel		if (read2) {
12396888Smarcel			n2 = getline(&line2, &line2len, fp2);
12496888Smarcel			if (n2 < 0 && ferror(fp2))
12596888Smarcel				err(1, "%s", argv[1]);
12696888Smarcel			if (n2 > 0 && line2[n2 - 1] == '\n')
12796888Smarcel				line2[n2 - 1] = '\0';
12896888Smarcel		}
12996888Smarcel
13096888Smarcel		/* if one file done, display the rest of the other file */
13196888Smarcel		if (n1 < 0) {
13296888Smarcel			if (n2 >= 0 && col2 != NULL)
13396888Smarcel				show(fp2, argv[1], col2, &line2, &line2len);
13496888Smarcel			break;
13596888Smarcel		}
13696888Smarcel		if (n2 < 0) {
13796888Smarcel			if (n1 >= 0 && col1 != NULL)
13896888Smarcel				show(fp1, argv[0], col1, &line1, &line1len);
13996888Smarcel			break;
14096888Smarcel		}
14196888Smarcel
14296888Smarcel		tline2 = NULL;
14396888Smarcel		if ((tline1 = convert(line1)) != NULL)
14496888Smarcel			tline2 = convert(line2);
14596888Smarcel		if (tline1 == NULL || tline2 == NULL)
14696888Smarcel			comp = strcmp(line1, line2);
14796888Smarcel		else
14896888Smarcel			comp = wcscoll(tline1, tline2);
14996888Smarcel		if (tline1 != NULL)
15096888Smarcel			free(tline1);
15196888Smarcel		if (tline2 != NULL)
15296888Smarcel			free(tline2);
15396888Smarcel
15496888Smarcel		/* lines are the same */
15596888Smarcel		if (!comp) {
15696888Smarcel			read1 = read2 = 1;
15796888Smarcel			if (col3 != NULL)
15896888Smarcel				(void)printf("%s%s\n", col3, line1);
15996888Smarcel			continue;
16096888Smarcel		}
16196888Smarcel
16296888Smarcel		/* lines are different */
16396888Smarcel		if (comp < 0) {
16496888Smarcel			read1 = 1;
16596888Smarcel			read2 = 0;
16696888Smarcel			if (col1 != NULL)
16796888Smarcel				(void)printf("%s%s\n", col1, line1);
16896888Smarcel		} else {
16996888Smarcel			read1 = 0;
17096888Smarcel			read2 = 1;
17196888Smarcel			if (col2 != NULL)
17296888Smarcel				(void)printf("%s%s\n", col2, line2);
17396888Smarcel		}
17496888Smarcel	}
17596888Smarcel	exit(0);
17696888Smarcel}
17796888Smarcel
17896888Smarcelstatic wchar_t *
17996888Smarcelconvert(const char *str)
18096888Smarcel{
18196888Smarcel	size_t n;
18296888Smarcel	wchar_t *buf, *p;
18396888Smarcel
18496888Smarcel	if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1)
18596888Smarcel		return (NULL);
18696888Smarcel	if (SIZE_MAX / sizeof(*buf) < n + 1)
18796888Smarcel		errx(1, "conversion buffer length overflow");
18896888Smarcel	if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL)
18996888Smarcel		err(1, "malloc");
19096888Smarcel	if (mbstowcs(buf, str, n + 1) != n)
19196888Smarcel		errx(1, "internal mbstowcs() error");
19296888Smarcel
19396888Smarcel	if (iflag) {
19496888Smarcel		for (p = buf; *p != L'\0'; p++)
19596888Smarcel			*p = towlower(*p);
19696888Smarcel	}
19796888Smarcel
19896888Smarcel	return (buf);
19996888Smarcel}
20096888Smarcel
20196888Smarcelstatic void
20296888Smarcelshow(FILE *fp, const char *fn, const char *offset, char **bufp, size_t *buflenp)
20396888Smarcel{
20496888Smarcel	ssize_t n;
20596888Smarcel
20696888Smarcel	do {
20796888Smarcel		(void)printf("%s%s\n", offset, *bufp);
20896888Smarcel		if ((n = getline(bufp, buflenp, fp)) < 0)
20996888Smarcel			break;
21096888Smarcel		if (n > 0 && (*bufp)[n - 1] == '\n')
21196888Smarcel			(*bufp)[n - 1] = '\0';
21296888Smarcel	} while (1);
21396888Smarcel	if (ferror(fp))
21496888Smarcel		err(1, "%s", fn);
21596888Smarcel}
21696888Smarcel
21796888Smarcelstatic FILE *
21896888Smarcelfile(const char *name)
21996888Smarcel{
22096888Smarcel	FILE *fp;
22196888Smarcel
22296888Smarcel	if (!strcmp(name, "-"))
22396888Smarcel		return (stdin);
224113868Sjhb	if ((fp = fopen(name, "r")) == NULL) {
225172207Sjeff		err(1, "%s", name);
226113868Sjhb	}
227276084Sjhb	return (fp);
228113868Sjhb}
22996888Smarcel
23096888Smarcelstatic void
23196888Smarcelusage(void)
23296888Smarcel{
23396888Smarcel	(void)fprintf(stderr, "usage: comm [-123i] file1 file2\n");
23496888Smarcel	exit(1);
235113868Sjhb}
236172207Sjeff