summaryrefslogblamecommitdiff
path: root/src/strings.c
blob: 5a1781b3c7e49d6e0ecae3bec52374b35063f2d3 (plain) (tree)



































































































































                                                                             
/*
 * 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;
}