GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ftp/complete.c Lines: 0 154 0.0 %
Date: 2017-11-13 Branches: 0 131 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: complete.c,v 1.31 2017/08/01 15:04:44 anton Exp $	*/
2
/*	$NetBSD: complete.c,v 1.10 1997/08/18 10:20:18 lukem Exp $	*/
3
4
/*-
5
 * Copyright (c) 1997 The NetBSD Foundation, Inc.
6
 * All rights reserved.
7
 *
8
 * This code is derived from software contributed to The NetBSD Foundation
9
 * by Luke Mewburn.
10
 *
11
 * Redistribution and use in source and binary forms, with or without
12
 * modification, are permitted provided that the following conditions
13
 * are met:
14
 * 1. Redistributions of source code must retain the above copyright
15
 *    notice, this list of conditions and the following disclaimer.
16
 * 2. Redistributions in binary form must reproduce the above copyright
17
 *    notice, this list of conditions and the following disclaimer in the
18
 *    documentation and/or other materials provided with the distribution.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
 * POSSIBILITY OF SUCH DAMAGE.
31
 */
32
33
#ifndef SMALL
34
35
/*
36
 * FTP user program - command and file completion routines
37
 */
38
39
#include <ctype.h>
40
#include <err.h>
41
#include <dirent.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
46
#include "ftp_var.h"
47
48
static int	     comparstr(const void *, const void *);
49
static unsigned char complete_ambiguous(char *, int, StringList *);
50
static unsigned char complete_command(char *, int);
51
static unsigned char complete_local(char *, int);
52
static unsigned char complete_remote(char *, int);
53
static void          ftpvis(char *, size_t, const char *, size_t);
54
55
static int
56
comparstr(const void *a, const void *b)
57
{
58
	return (strcmp(*(char **)a, *(char **)b));
59
}
60
61
/*
62
 * Determine if complete is ambiguous. If unique, insert.
63
 * If no choices, error. If unambiguous prefix, insert that.
64
 * Otherwise, list choices. words is assumed to be filtered
65
 * to only contain possible choices.
66
 * Args:
67
 *	word	word which started the match
68
 *	list	list by default
69
 *	words	stringlist containing possible matches
70
 */
71
static unsigned char
72
complete_ambiguous(char *word, int list, StringList *words)
73
{
74
	char insertstr[PATH_MAX * 2];
75
	char *lastmatch;
76
	int i, j;
77
	size_t matchlen, wordlen;
78
79
	wordlen = strlen(word);
80
	if (words->sl_cur == 0)
81
		return (CC_ERROR);	/* no choices available */
82
83
	if (words->sl_cur == 1) {	/* only once choice available */
84
		char *p = words->sl_str[0] + wordlen;
85
		ftpvis(insertstr, sizeof(insertstr), p, strlen(p));
86
		if (el_insertstr(el, insertstr) == -1)
87
			return (CC_ERROR);
88
		else
89
			return (CC_REFRESH);
90
	}
91
92
	if (!list) {
93
		lastmatch = words->sl_str[0];
94
		matchlen = strlen(lastmatch);
95
		for (i = 1 ; i < words->sl_cur ; i++) {
96
			for (j = wordlen ; j < strlen(words->sl_str[i]); j++)
97
				if (lastmatch[j] != words->sl_str[i][j])
98
					break;
99
			if (j < matchlen)
100
				matchlen = j;
101
		}
102
		if (matchlen > wordlen) {
103
			ftpvis(insertstr, sizeof(insertstr),
104
			    lastmatch + wordlen, matchlen - wordlen);
105
			if (el_insertstr(el, insertstr) == -1)
106
				return (CC_ERROR);
107
			else
108
					/*
109
					 * XXX: really want CC_REFRESH_BEEP
110
					 */
111
				return (CC_REFRESH);
112
		}
113
	}
114
115
	putc('\n', ttyout);
116
	qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr);
117
	list_vertical(words);
118
	return (CC_REDISPLAY);
119
}
120
121
/*
122
 * Complete a command
123
 */
124
static unsigned char
125
complete_command(char *word, int list)
126
{
127
	struct cmd *c;
128
	StringList *words;
129
	size_t wordlen;
130
	unsigned char rv;
131
132
	words = sl_init();
133
	wordlen = strlen(word);
134
135
	for (c = cmdtab; c->c_name != NULL; c++) {
136
		if (wordlen > strlen(c->c_name))
137
			continue;
138
		if (strncmp(word, c->c_name, wordlen) == 0)
139
			sl_add(words, c->c_name);
140
	}
141
142
	rv = complete_ambiguous(word, list, words);
143
	sl_free(words, 0);
144
	return (rv);
145
}
146
147
/*
148
 * Complete a local file
149
 */
150
static unsigned char
151
complete_local(char *word, int list)
152
{
153
	StringList *words;
154
	char dir[PATH_MAX];
155
	char *file;
156
	DIR *dd;
157
	struct dirent *dp;
158
	unsigned char rv;
159
160
	if ((file = strrchr(word, '/')) == NULL) {
161
		dir[0] = '.';
162
		dir[1] = '\0';
163
		file = word;
164
	} else {
165
		if (file == word) {
166
			dir[0] = '/';
167
			dir[1] = '\0';
168
		} else {
169
			(void)strlcpy(dir, word, (size_t)(file - word) + 1);
170
		}
171
		file++;
172
	}
173
174
	if ((dd = opendir(dir)) == NULL)
175
		return (CC_ERROR);
176
177
	words = sl_init();
178
179
	for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
180
		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
181
			continue;
182
		if (strlen(file) > dp->d_namlen)
183
			continue;
184
		if (strncmp(file, dp->d_name, strlen(file)) == 0) {
185
			char *tcp;
186
187
			tcp = strdup(dp->d_name);
188
			if (tcp == NULL)
189
				errx(1, "Can't allocate memory for local dir");
190
			sl_add(words, tcp);
191
		}
192
	}
193
	closedir(dd);
194
195
	rv = complete_ambiguous(file, list, words);
196
	sl_free(words, 1);
197
	return (rv);
198
}
199
200
/*
201
 * Complete a remote file
202
 */
203
static unsigned char
204
complete_remote(char *word, int list)
205
{
206
	static StringList *dirlist;
207
	static char	 lastdir[PATH_MAX];
208
	StringList	*words;
209
	char		 dir[PATH_MAX];
210
	char		*file, *cp;
211
	int		 i;
212
	unsigned char	 rv;
213
214
	char *dummyargv[] = { "complete", dir, NULL };
215
216
	if ((file = strrchr(word, '/')) == NULL) {
217
		dir[0] = '.';
218
		dir[1] = '\0';
219
		file = word;
220
	} else {
221
		cp = file;
222
		while (*cp == '/' && cp > word)
223
			cp--;
224
		(void)strlcpy(dir, word, (size_t)(cp - word + 2));
225
		file++;
226
	}
227
228
	if (dirchange || strcmp(dir, lastdir) != 0) {	/* dir not cached */
229
		char *emesg;
230
231
		sl_free(dirlist, 1);
232
		dirlist = sl_init();
233
234
		mflag = 1;
235
		emesg = NULL;
236
		if (debug)
237
			(void)putc('\n', ttyout);
238
		while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) {
239
			char *tcp;
240
241
			if (!mflag)
242
				continue;
243
			if (*cp == '\0') {
244
				mflag = 0;
245
				continue;
246
			}
247
			tcp = strrchr(cp, '/');
248
			if (tcp)
249
				tcp++;
250
			else
251
				tcp = cp;
252
			tcp = strdup(tcp);
253
			if (tcp == NULL)
254
				errx(1, "Can't allocate memory for remote dir");
255
			sl_add(dirlist, tcp);
256
		}
257
		if (emesg != NULL) {
258
			fprintf(ttyout, "\n%s\n", emesg);
259
			return (CC_REDISPLAY);
260
		}
261
		(void)strlcpy(lastdir, dir, sizeof lastdir);
262
		dirchange = 0;
263
	}
264
265
	words = sl_init();
266
	for (i = 0; i < dirlist->sl_cur; i++) {
267
		cp = dirlist->sl_str[i];
268
		if (strlen(file) > strlen(cp))
269
			continue;
270
		if (strncmp(file, cp, strlen(file)) == 0)
271
			sl_add(words, cp);
272
	}
273
	rv = complete_ambiguous(file, list, words);
274
	sl_free(words, 0);
275
	return (rv);
276
}
277
278
/*
279
 * Generic complete routine
280
 */
281
unsigned char
282
complete(EditLine *el, int ch)
283
{
284
	static char word[FTPBUFLEN];
285
	static int lastc_argc, lastc_argo;
286
	struct cmd *c;
287
	const LineInfo *lf;
288
	int celems, dolist;
289
	size_t len;
290
291
	lf = el_line(el);
292
	len = lf->lastchar - lf->buffer;
293
	if (len >= sizeof(line))
294
		return (CC_ERROR);
295
	(void)memcpy(line, lf->buffer, len);
296
	line[len] = '\0';
297
	cursor_pos = line + (lf->cursor - lf->buffer);
298
	lastc_argc = cursor_argc;	/* remember last cursor pos */
299
	lastc_argo = cursor_argo;
300
	makeargv();			/* build argc/argv of current line */
301
302
	if (cursor_argo >= sizeof(word))
303
		return (CC_ERROR);
304
305
	dolist = 0;
306
			/* if cursor and word is same, list alternatives */
307
	if (lastc_argc == cursor_argc && lastc_argo == cursor_argo
308
	    && strncmp(word, margv[cursor_argc], cursor_argo) == 0)
309
		dolist = 1;
310
	else if (cursor_argo)
311
		memcpy(word, margv[cursor_argc], cursor_argo);
312
	word[cursor_argo] = '\0';
313
314
	if (cursor_argc == 0)
315
		return (complete_command(word, dolist));
316
317
	c = getcmd(margv[0]);
318
	if (c == (struct cmd *)-1 || c == 0)
319
		return (CC_ERROR);
320
	celems = strlen(c->c_complete);
321
322
		/* check for 'continuation' completes (which are uppercase) */
323
	if ((cursor_argc > celems) && (celems > 0)
324
	    && isupper((unsigned char)c->c_complete[celems - 1]))
325
		cursor_argc = celems;
326
327
	if (cursor_argc > celems)
328
		return (CC_ERROR);
329
330
	switch (c->c_complete[cursor_argc - 1]) {
331
	case 'l':			/* local complete */
332
	case 'L':
333
		return (complete_local(word, dolist));
334
	case 'r':			/* remote complete */
335
	case 'R':
336
		if (connected != -1) {
337
			fputs("\nMust be logged in to complete.\n", ttyout);
338
			return (CC_REDISPLAY);
339
		}
340
		return (complete_remote(word, dolist));
341
	case 'c':			/* command complete */
342
	case 'C':
343
		return (complete_command(word, dolist));
344
	case 'n':			/* no complete */
345
		return (CC_ERROR);
346
	}
347
348
	return (CC_ERROR);
349
}
350
351
/*
352
 * Copy characters from src into dst, \ quoting characters that require it.
353
 */
354
static void
355
ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
356
{
357
	size_t	di, si;
358
359
	di = si = 0;
360
	while (di + 1 < dstlen && si < srclen && src[si] != '\0') {
361
		switch (src[si]) {
362
		case '\\':
363
		case ' ':
364
		case '\t':
365
		case '\r':
366
		case '\n':
367
		case '"':
368
			/* Need room for two characters and NUL, avoiding
369
			 * incomplete escape sequences at end of dst. */
370
			if (di + 3 >= dstlen)
371
				break;
372
			dst[di++] = '\\';
373
			/* FALLTHROUGH */
374
		default:
375
			dst[di++] = src[si++];
376
		}
377
	}
378
	if (dstlen != 0)
379
		dst[di] = '\0';
380
}
381
#endif /* !SMALL */