1/*
2 * Copyright (C) 1984-2023  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * lessecho [-ox] [-cx] [-pn] [-dn] [-a] file ...
13 * Simply echos its filename arguments on standard output.
14 * But any argument containing spaces is enclosed in quotes.
15 *
16 * -ox  Specifies "x" to be the open quote character.
17 * -cx  Specifies "x" to be the close quote character.
18 * -pn  Specifies "n" to be the open quote character, as an integer.
19 * -dn  Specifies "n" to be the close quote character, as an integer.
20 * -mx  Specifies "x" to be a metachar.
21 * -nn  Specifies "n" to be a metachar, as an integer.
22 * -ex  Specifies "x" to be the escape char for metachars.
23 * -fn  Specifies "x" to be the escape char for metachars, as an integer.
24 * -a   Specifies that all arguments are to be quoted.
25 *      The default is that only arguments containing spaces are quoted.
26 */
27
28#include "less.h"
29
30static char *version = "$Revision: 1.15 $";
31
32static int quote_all = 0;
33static char openquote = '"';
34static char closequote = '"';
35static char *meta_escape = "\\";
36static char meta_escape_buf[2];
37static char* metachars = NULL;
38static int num_metachars = 0;
39static int size_metachars = 0;
40
41static void pr_usage(void)
42{
43	fprintf(stderr,
44		"usage: lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-fn] [-a] file ...\n");
45}
46
47static void pr_version(void)
48{
49	char *p;
50	char buf[10];
51	char *pbuf = buf;
52
53	for (p = version;  *p != ' ';  p++)
54		if (*p == '\0')
55			return;
56	for (p++;  *p != '$' && *p != ' ' && *p != '\0';  p++)
57		*pbuf++ = *p;
58	*pbuf = '\0';
59	printf("%s\n", buf);
60}
61
62static void pr_error(char *s)
63{
64	fprintf(stderr, "%s\n", s);
65	exit(1);
66}
67
68static long lstrtol(char *s, char **pend, int radix)
69{
70	int v;
71	int neg = 0;
72	long n = 0;
73
74	/* Skip leading white space. */
75	while (*s == ' ' || *s == '\t')
76		s++;
77
78	/* Check for a leading + or -. */
79	if (*s == '-')
80	{
81		neg = 1;
82		s++;
83	} else if (*s == '+')
84	{
85		s++;
86	}
87
88	/* Determine radix if caller does not specify. */
89	if (radix == 0)
90	{
91		radix = 10;
92		if (*s == '0')
93		{
94			switch (*++s)
95			{
96			case 'x':
97				radix = 16;
98				s++;
99				break;
100			default:
101				radix = 8;
102				break;
103			}
104		}
105	}
106
107	/* Parse the digits of the number. */
108	for (;;)
109	{
110		if (*s >= '0' && *s <= '9')
111			v = *s - '0';
112		else if (*s >= 'a' && *s <= 'f')
113			v = *s - 'a' + 10;
114		else if (*s >= 'A' && *s <= 'F')
115			v = *s - 'A' + 10;
116		else
117			break;
118		if (v >= radix)
119			break;
120		n = n * radix + v;
121		s++;
122	}
123
124	if (pend != NULL)
125	{
126		/* Skip trailing white space. */
127		while (*s == ' ' || *s == '\t')
128			s++;
129		*pend = s;
130	}
131	if (neg)
132		return (-n);
133	return (n);
134}
135
136static void add_metachar(int ch)
137{
138	if (num_metachars+1 >= size_metachars)
139	{
140		char *p;
141		size_metachars = (size_metachars > 0) ? size_metachars*2 : 16;
142		p = (char *) malloc(size_metachars);
143		if (p == NULL)
144			pr_error("Cannot allocate memory");
145
146		if (metachars != NULL)
147		{
148			strcpy(p, metachars);
149			free(metachars);
150		}
151		metachars = p;
152	}
153	metachars[num_metachars++] = ch;
154	metachars[num_metachars] = '\0';
155}
156
157static int is_metachar(int ch)
158{
159	return (metachars != NULL && strchr(metachars, ch) != NULL);
160}
161
162#if !HAVE_STRCHR
163char * strchr(char *s, char c)
164{
165	for ( ;  *s != '\0';  s++)
166		if (*s == c)
167			return (s);
168	if (c == '\0')
169		return (s);
170	return (NULL);
171}
172#endif
173
174int main(int argc, char *argv[])
175{
176	char *arg;
177	char *s;
178	int no_more_options;
179
180	no_more_options = 0;
181	while (--argc > 0)
182	{
183		arg = *++argv;
184		if (*arg != '-' || no_more_options)
185			break;
186		switch (*++arg)
187		{
188		case 'a':
189			quote_all = 1;
190			break;
191		case 'c':
192			closequote = *++arg;
193			break;
194		case 'd':
195			closequote = lstrtol(++arg, &s, 0);
196			if (s == arg)
197				pr_error("Missing number after -d");
198			break;
199		case 'e':
200			if (strcmp(++arg, "-") == 0)
201				meta_escape = "";
202			else
203				meta_escape = arg;
204			break;
205		case 'f':
206			meta_escape_buf[0] = lstrtol(++arg, &s, 0);
207			meta_escape_buf[1] = '\0';
208			meta_escape = meta_escape_buf;
209			if (s == arg)
210				pr_error("Missing number after -f");
211			break;
212		case 'o':
213			openquote = *++arg;
214			break;
215		case 'p':
216			openquote = lstrtol(++arg, &s, 0);
217			if (s == arg)
218				pr_error("Missing number after -p");
219			break;
220		case 'm':
221			add_metachar(*++arg);
222			break;
223		case 'n':
224			add_metachar(lstrtol(++arg, &s, 0));
225			if (s == arg)
226				pr_error("Missing number after -n");
227			break;
228		case '?':
229			pr_usage();
230			return (0);
231		case '-':
232			if (*++arg == '\0')
233			{
234				no_more_options = 1;
235				break;
236			}
237			if (strcmp(arg, "version") == 0)
238			{
239				pr_version();
240				return (0);
241			}
242			if (strcmp(arg, "help") == 0)
243			{
244				pr_usage();
245				return (0);
246			}
247			pr_error("Invalid option after --");
248		default:
249			pr_error("Invalid option letter");
250		}
251	}
252
253	while (argc-- > 0)
254	{
255		int has_meta = 0;
256		arg = *argv++;
257		for (s = arg;  *s != '\0';  s++)
258		{
259			if (is_metachar(*s))
260			{
261				has_meta = 1;
262				break;
263			}
264		}
265		if (quote_all || (has_meta && strlen(meta_escape) == 0))
266			printf("%c%s%c", openquote, arg, closequote);
267		else
268		{
269			for (s = arg;  *s != '\0';  s++)
270			{
271				if (is_metachar(*s))
272					printf("%s", meta_escape);
273				printf("%c", *s);
274			}
275		}
276		if (argc > 0)
277			printf(" ");
278		else
279			printf("\n");
280	}
281	return (0);
282}
283