summaryrefslogtreecommitdiff
path: root/src/strings.c
blob: 5a1781b3c7e49d6e0ecae3bec52374b35063f2d3 (plain) (blame)
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * strings.c
 * Implementation of SUSv3 XCU strings utility
 * Copyright © 2007 Rich Felker
 * Licensed under the terms of the GNU General Public License, v2 or later
 */

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <wchar.h>
#include <wctype.h>
#include <unistd.h>
#include <limits.h>
#include <locale.h>

static int my_mbrtowc(wchar_t *wc, int b, mbstate_t *st)
{
	char c = b;
	int retry = 1;
retry:
	switch ((int)mbrtowc(wc, &c, 1, st)) {
	case -2:
		return -2;
	case -1:
		memset(st, 0, sizeof(mbstate_t));
		if (retry--) goto retry;
		return retry; /* yes this is lame */
	}
	return 1;
}

int main(int argc, char *argv[])
{
	mbstate_t st;
	wchar_t *buf;
	size_t i, min = 4;
	int b;
	off_t start, ofs;
	int l;
	char fmtbuf[] = "%ll? ", *fmt=fmtbuf+5;
	int ret = 0;
	int in_str;
	FILE *f = stdin;
	char *name = "(stdin)";

	if (!setlocale(LC_CTYPE, "")) {
		fprintf(stderr, "%s: warning: cannot set LC_CTYPE", argv[0]);
		perror("");
	}

	while ((b = getopt(argc, argv, "an:t:")) != EOF) switch(b) {
	case 'a':
		/* no-op: we always search entire file */
		break;
	case 'n':
		min = strtoul(optarg, NULL, 10);
		if (min < 1 || min >= SIZE_MAX) {
			fprintf(stderr, "%s: invalid argument to -%c: %s\n",
			argv[0], b, optarg);
			exit(1);
		}
		break;
	case 't':
		if (!strchr("dox", optarg[0]) || optarg[1]) {
			fprintf(stderr, "%s: invalid argument to -%c: %s\n",
				argv[0], b, optarg);
			exit(1);
		}
		fmt = fmtbuf;
		fmt[3] = optarg[0];
		break;
	default:
		exit(1);
	}

	/* min+1 does not overflow because of above check */
	if (!(buf = calloc(sizeof(wchar_t), min+1))) {
		fprintf(stderr, "%s: ", argv[0]);
		perror("calloc failed");
		exit(1);
	}

	if (optind < argc) goto nextfile;
begin:	
	memset(&st, 0, sizeof(mbstate_t));
	start = ofs = i = in_str = 0;
	while ((b=getc(f)) >= 0) {
		ofs++;
		l = my_mbrtowc(buf+i, b, &st);
		if (l == -2) continue;
		if (l == -1 || !iswprint(buf[i])) {
			if (in_str) putchar('\n');
			in_str = 0;
			i = 0;
			start = ofs;
			continue;
		}
		if (i<min-1) {
			i++;
			continue;
		}
		if (!in_str) {
			in_str = 1;
			printf(fmt, (long long)start);
			printf("%ls", buf);
			continue;
		}
		printf("%lc", buf[i]);
	}
	if (ferror(f)) {
		fprintf(stderr, "%s: error reading file %s", argv[0], name);
		perror("");
		ret = 1;
	}
	fclose(f);
nextfile:
	if (optind < argc) {
		name = argv[optind++];
		if (!(f = fopen(name, "rb"))) {
			fprintf(stderr, "%s: error opening file %s",
				argv[0], name);
			perror("");
			ret = 1;
			goto nextfile;
		}
		goto begin;
	}

	return ret;
}