1 #include "helpers.h"
2 
3 #include <stdio.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 
7 #ifdef __OpenBSD__
8 typedef char *rl_cpvfunc_t;
9 #include <ereadline/readline/readline.h>
10 #else
11 #include <readline/readline.h>
12 #endif
13 #include <errno.h>
14 #include <fcntl.h>
15 
16 //#include <curses.h>
17 
18 #include "exec.h"
19 #include "aux.h"
20 #include "misc.h"
21 #include "checks.h"
22 #include "strings.h"
23 #include "colors.h"
24 #include "navigation.h"
25 
26 #ifndef _NO_HIGHLIGHT
27 #include "highlight.h"
28 #endif
29 
30 #ifndef _NO_SUGGESTIONS
31 #include "suggestions.h"
32 #endif
33 
34 #define PUTX(c) \
35 	if (CTRL_CHAR(c)) { \
36           putc('^', rl_outstream); \
37           putc(UNCTRL(c), rl_outstream); \
38 	} else if (c == RUBOUT) { \
39 		putc('^', rl_outstream); \
40 		putc('?', rl_outstream); \
41 	} else \
42 		putc(c, rl_outstream)
43 
44 /* Return the character which best describes FILENAME.
45      `@' for symbolic links
46      `/' for directories
47      `*' for executables
48      `=' for sockets */
49 static int
stat_char(char * filename)50 stat_char(char *filename)
51 {
52 	struct stat attr;
53 	int r;
54 
55 #if defined(S_ISLNK)
56 	r = lstat(filename, &attr);
57 #else
58 	r = stat(filename, &attr);
59 #endif
60 
61 	if (r == -1)
62 		return 0;
63 
64 	int c = 0;
65 	if (S_ISDIR(attr.st_mode)) {
66 		c = '/';
67 #if defined(S_ISLNK)
68 	} else if (S_ISLNK(attr.st_mode)) {
69 		c = '@';
70 #endif /* S_ISLNK */
71 #if defined(S_ISSOCK)
72 	} else if (S_ISSOCK(attr.st_mode)) {
73 		c = '=';
74 #endif /* S_ISSOCK */
75 	} else if (S_ISREG(attr.st_mode)) {
76 		if (access(filename, X_OK) == 0)
77 			c = '*';
78 #if defined(S_ISFIFO)
79 	} else if (S_ISFIFO(attr.st_mode)) {
80 		c = '|';
81 #endif /* S_ISFIFO */
82 /*#if defined(S_ISBLK)
83 	} else if (S_ISBLK(attr.st_mode)) {
84 		c = '%';
85 #endif
86 #if defined(S_ISCHR)
87 	} else if (S_ISCHR(attr.st_mode)) {
88 		c = '&';
89 #endif */
90 	}
91 
92 	return c;
93 }
94 
95 /* Stupid comparison routine for qsort () ing strings. */
96 /*static int
97 compare_strings(const void *s1, const void *s2)
98 {
99 	int result;
100 	char *ss1 = (char *)s1, *ss2 = (char *)s2;
101 
102 	result = (int)(ss1 - ss2);
103 	if (result == 0)
104 		result = strcmp(s1, s2);
105 
106 	return result;
107 } */
108 
109 /* The user must press "y" or "n". Non-zero return means "y" pressed. */
110 static int
get_y_or_n(void)111 get_y_or_n(void)
112 {
113 	for (;;) {
114 		int c = fgetc(stdin);
115 		if (c == 'y' || c == 'Y' || c == ' ')
116 			return (1);
117 		 if (c == 'n' || c == 'N' || c == RUBOUT) {
118 			putchar('\n');
119 			return (0);
120 		}
121 		if (c == ABORT_CHAR)
122 			rl_abort(0, 0);
123 		rl_ding();
124 	}
125 }
126 
127 static int
print_filename(char * to_print,char * full_pathname)128 print_filename(char *to_print, char *full_pathname)
129 {
130 	char *s;
131 
132 	if (colorize && (cur_comp_type == TCMP_PATH || cur_comp_type == TCMP_SEL
133 	|| cur_comp_type == TCMP_DESEL || cur_comp_type == TCMP_RANGES)) {
134 		colors_list(to_print, 0, 0, 0);
135 	} else {
136 		for (s = to_print + tab_offset; *s; s++) {
137 			PUTX(*s);
138 		}
139 	}
140 
141 /*	int rl_visible_stats = 1; */
142 	if (rl_filename_completion_desired && !colorize) {
143 		if (cur_comp_type == TCMP_CMD) {
144 			putc('*', rl_outstream);
145 			return 1;
146 		}
147       /* If to_print != full_pathname, to_print is the basename of the
148 	 path passed.  In this case, we try to expand the directory
149 	 name before checking for the stat character. */
150 		int extension_char = 0;
151 		if (to_print != full_pathname) {
152 		/* Terminate the directory name. */
153 			char c = to_print[-1];
154 			to_print[-1] = '\0';
155 
156 			s = tilde_expand(full_pathname);
157 			if (rl_directory_completion_hook)
158 				(*rl_directory_completion_hook) (&s);
159 
160 			size_t slen = strlen(s);
161 			size_t tlen = strlen(to_print);
162 			char *new_full_pathname = (char *)xnmalloc(slen + tlen + 2, sizeof(char));
163 			strcpy(new_full_pathname, s);
164 			new_full_pathname[slen] = '/';
165 			strcpy(new_full_pathname + slen + 1, to_print);
166 
167 			extension_char = stat_char(new_full_pathname);
168 
169 			free(new_full_pathname);
170 			to_print[-1] = c;
171 		} else {
172 			s = tilde_expand(full_pathname);
173 			extension_char = stat_char(s);
174 		}
175 
176 		free(s);
177 		if (extension_char)
178 			putc(extension_char, rl_outstream);
179 		return (extension_char != 0);
180 	} else {
181 		return 0;
182 	}
183 }
184 
185 /* Return the portion of PATHNAME that should be output when listing
186    possible completions.  If we are hacking filename completion, we
187    are only interested in the basename, the portion following the
188    final slash.  Otherwise, we return what we were passed. */
189 static char *
printable_part(char * pathname)190 printable_part(char *pathname)
191 {
192 	char *temp = (char *)NULL;
193 
194 	if (rl_filename_completion_desired)
195 		temp = strrchr(pathname, '/');
196 
197 	if (!temp)
198 		return (pathname);
199 	else
200 		return (++temp);
201 }
202 
203 /* Find the first occurrence in STRING1 of any character from STRING2.
204    Return a pointer to the character in STRING1. */
205 static char *
rl_strpbrk(char * s1,char * s2)206 rl_strpbrk(char *s1, char *s2)
207 {
208 	register char *scan;
209 
210 	for (; *s1; s1++) {
211 		for (scan = s2; *scan; scan++) {
212 			if (*s1 == *scan) {
213 				return (s1);
214 			}
215 		}
216 	}
217 	return (char *)NULL;
218 }
219 
220 #ifndef _NO_FZF
221 #define FZFTABIN "/tmp/clifm.fzf.in"
222 #define FZFTABOUT "/tmp/clifm.fzf.out"
223 
224 static char *
fzftab_color(char * filename,const struct stat attr)225 fzftab_color(char *filename, const struct stat attr)
226 {
227 	switch(attr.st_mode & S_IFMT) {
228 	case S_IFDIR:
229 		if (!check_file_access(attr))
230 			return nd_c;
231 		return get_dir_color(filename, attr.st_mode);
232 	case S_IFREG:
233 		if (!check_file_access(attr))
234 			return nf_c;
235 		char *ext_cl = (char *)NULL;
236 		char *ext = strrchr(filename, '.');
237 		if (ext && ext != filename)
238 			ext_cl = get_ext_color(ext);
239 		if (ext_cl)
240 			return ext_cl;
241 		return get_file_color(filename, attr);
242 	case S_IFSOCK: return so_c;
243 	case S_IFIFO: return pi_c;
244 	case S_IFBLK: return bd_c;
245 	case S_IFCHR: return cd_c;
246 	case S_IFLNK: return ln_c;
247 	default: return uf_c;
248 	}
249 }
250 
251 static inline char *
get_entry_color(char ** matches,const size_t i)252 get_entry_color(char **matches, const size_t i)
253 {
254 	struct stat attr;
255 	char *cl = (char *)NULL;
256 
257 	if (*matches[i] == '/') { /* Absolute path */
258 		if (colorize && (cur_comp_type == TCMP_PATH
259 		|| cur_comp_type == TCMP_SEL || cur_comp_type == TCMP_DESEL)) {
260 			if (lstat(matches[i], &attr) != -1)
261 				cl = fzftab_color(matches[i], attr);
262 		}
263 	} else if (*matches[i] == '~') {  /* Tilde */
264 		if (colorize && cur_comp_type == TCMP_PATH) {
265 			char *exp_path = tilde_expand(matches[i]);
266 			if (exp_path) {
267 				char tmp_path[PATH_MAX + 1];
268 				strncpy(tmp_path, exp_path, PATH_MAX);
269 				free(exp_path);
270 				if (lstat(tmp_path, &attr) != -1)
271 					cl = fzftab_color(tmp_path, attr);
272 			}
273 		}
274 	} else { /* Relative path */
275 		if (colorize) {
276 			if (cur_comp_type == TCMP_PATH || cur_comp_type == TCMP_RANGES) {
277 				char tmp_path[PATH_MAX];
278 				snprintf(tmp_path, PATH_MAX, "%s/%s", ws[cur_ws].path, matches[i]);
279 				if (lstat(tmp_path, &attr) != -1)
280 					cl = fzftab_color(tmp_path, attr);
281 			} else if (cur_comp_type == TCMP_CMD) {
282 				if (is_internal_c(matches[i]))
283 					cl = hv_c;
284 			}
285 		}
286 	}
287 
288 	return cl;
289 }
290 
291 static inline void
write_completion(char * buf,const size_t * offset,int * exit_status)292 write_completion(char *buf, const size_t *offset, int *exit_status)
293 {
294 	/* Remove ending new line char */
295 	char *n = strchr(buf, '\n');
296 	if (n)
297 		*n = '\0';
298 	if (cur_comp_type == TCMP_PATH) {
299 		char *esc_buf = escape_str(buf);
300 		if (esc_buf) {
301 			rl_insert_text(esc_buf + *offset);
302 			free(esc_buf);
303 		} else {
304 			rl_insert_text(buf + *offset);
305 		}
306 	} else {
307 		rl_insert_text(buf + *offset);
308 	}
309 
310 	/* Append slash for dirs and space for non-dirs */
311 	char *pp = rl_line_buffer;
312 	char *ss = (char *)NULL;
313 	if (pp) {
314 		while (*pp) {
315 			if (pp == rl_line_buffer) {
316 				pp++;
317 				continue;
318 			}
319 
320 			if (*pp == ' ' && *(pp - 1) != '\\' && *(pp + 1) != ' ')
321 				ss = pp + 1;
322 
323 			pp++;
324 		}
325 	}
326 
327 	if (!ss || !*ss)
328 		ss = rl_line_buffer;
329 
330 	if (!ss)
331 		return;
332 
333 	char deq_str[PATH_MAX];
334 	*deq_str = '\0';
335 	if (strchr(ss, '\\')) {
336 		size_t i = 0;
337 		char *b = ss;
338 		while (*b && i < (PATH_MAX - 1)) {
339 			if (*b != '\\')
340 				deq_str[i++] = *b;
341 			b++;
342 		}
343 		deq_str[i] = '\0';
344 	}
345 
346 	char _path[PATH_MAX];
347 	*_path = '\0';
348 	if (*ss != '/' && *ss != '.' && *ss != '~')
349 		snprintf(_path, PATH_MAX, "%s/%s", ws[cur_ws].path, ss);
350 
351 	char *spath = *_path ? _path : (*deq_str ? deq_str : ss);
352 	char *epath = (char *)NULL;
353 	if (*spath == '~')
354 		epath = tilde_expand(spath);
355 	else if (*spath == '.') {
356 		xchdir(ws[cur_ws].path, NO_TITLE);
357 		epath = realpath(spath, NULL);
358 		/* No need to change back to CWD. Done here */
359 		*exit_status = -1;
360 	}
361 
362 	if (epath)
363 		spath = epath;
364 
365 	struct stat attr;
366 	if (stat(spath, &attr) != -1 && S_ISDIR(attr.st_mode))
367 		rl_insert_text("/");
368 	else if (cur_comp_type != TCMP_OPENWITH)
369 		rl_stuff_char(' ');
370 
371 	free(epath);
372 }
373 
374 static inline char *
get_last_word(char * matches)375 get_last_word(char *matches)
376 {
377 	/* Get word after last non-escaped space */
378 	char *ss = matches, *s = (char *)NULL;
379 	while (*ss) {
380 		if (ss == matches) {
381 			ss++;
382 			continue;
383 		}
384 		if (*ss == ' ' && *(ss - 1) != '\\' && *(ss + 1) != ' ')
385 			s = ss;
386 		ss++;
387 	}
388 	if (!s)
389 		s = matches;
390 
391 	/* Get word after last non-escaped slash */
392 	char *sl = s;
393 	char *d = (char *)NULL;
394 	while (*sl) {
395 		if (sl == s) {
396 			if (*sl == '/')
397 				d = sl;
398 		} else if (*sl == '/' && *(sl - 1) != '\\') {
399 			d = sl;
400 		}
401 		sl++;
402 	}
403 
404 	if (!d)
405 		d = s;
406 	else if (*d == '/')
407 		d++;
408 
409 	return d;
410 }
411 
412 static inline int
run_fzf(const size_t * height,const int * offset,const char * lw)413 run_fzf(const size_t *height, const int *offset, const char *lw)
414 {
415 	char cmd[PATH_MAX];
416 	snprintf(cmd, PATH_MAX, "$(fzf %s "
417 			"--height=%zu --margin=0,0,0,%d "
418 			"%s "
419 			"--query=\"%s\" "
420 			"< %s > %s)",
421 			fzftab_options,
422 			*height, *offset,
423 			case_sens_path_comp ? "+i" : "-i",
424 			lw ? lw : "",
425 			FZFTABIN, FZFTABOUT);
426 	int ret = launch_execle(cmd);
427 
428 	return ret;
429 }
430 
431 /*
432 static int
433 curses_tab(char **list)
434 {
435 	if (!list || !list[0])
436 		return (-1);
437 
438 	get_cursor_position(STDIN_FILENO, STDOUT_FILENO);
439 
440 	char *l = list[0];
441 	list++;
442 
443 	size_t n = 0;
444 	int i = 0;
445 	for (; list[n]; n++);
446 
447 	WINDOW *w;
448 	char item[PATH_MAX];
449 	int c;
450 
451 	size_t height = 0;
452 	if ((int)n + 1 > term_rows - 2)
453 		height = term_rows - 2;
454 	else
455 		height = n;
456 
457 	// Calculate the offset (left padding) of the FZF window based on
458 	// cursor position and current query string
459 	int max_curses_offset = term_cols > 20 ? term_cols - 20 : 0;
460 	int curses_offset = (rl_point + prompt_offset < max_curses_offset)
461 			? (rl_point + prompt_offset - 4) : 0;
462 
463 	// Calculate currently used lines to go back to the correct cursor
464 	// position after quitting FZF
465 	int lines = 1, total_line_len = 0;
466 	total_line_len = rl_end + prompt_offset;
467 	// PROMPT_OFFSET (the space used by the prompt in the current line)
468 	// is calculated the first time we print the prompt (in my_rl_getc
469 	// (readline.c))
470 
471 	if (total_line_len > term_cols) {
472 		lines = total_line_len / term_cols;
473 		int rem = (int)total_line_len % term_cols;
474 		if (rem > 0)
475 			lines++;
476 	}
477 
478 //	if (currow + (int)height > term_rows) {
479 //		printf("\x1b[%dB", term_rows - (currow + (int)height));
480 //	}
481 
482 	int a = (int)height <= term_rows -2 ? (int)height : term_rows - 2;
483 
484 	initscr(); // initialize Ncurses
485 //	start_color();
486 //	init_pair(1, COLOR_RED, COLOR_BLACK);
487 //	init_pair(2, COLOR_CYAN, COLOR_BLACK);
488 
489 	w = newwin(a + 1, PATH_MAX, 1, curses_offset); // create a new window
490 //	box(w, 0, 0); // sets default borders for the window
491 
492 	// now print all the menu items and highlight the first one
493 	for (i = 0; i < (int)n; i++) {
494 		if (i == 0)
495 			wattron(w, A_STANDOUT); // highlights the first item.
496 		else
497 			wattroff(w, A_STANDOUT);
498 		sprintf(item, "%-7s",  list[i]);
499 		mvwprintw(w, i + 1, 2, "%s", item);
500 	}
501 
502 	wrefresh(w); // update the terminal screen
503 
504 	i = 0;
505 	scrollok(w, TRUE);
506 	noecho(); // disable echoing of characters on the screen
507 	keypad(w, TRUE); // enable keyboard input for the window.
508 	curs_set(0); // hide the default screen cursor.
509 
510 	// get the input
511 	while ((c = wgetch(w))) {
512 		// right pad with spaces to make the items appear with even width.
513 		sprintf(item, "%-7s",  list[i]);
514 		mvwprintw(w, i + 1, 2, "%s", item);
515 		// use a variable to increment or decrement the value based on the input.
516 		switch(c) {
517 		case KEY_UP:
518 			i--;
519 			i = i < 0 ? (int)n - 1 : i;
520 //			if (i > term_rows - 3) {
521 //				wscrl(w, -1);
522 //				wrefresh(w);
523 //			}
524 		break;
525 		case KEY_DOWN:
526 			i++;
527 			i = i > (int)n - 1 ? 0 : i;
528 //			if (i > term_rows - 3) {
529 //				wscrl(w, 1);
530 //				wrefresh(w);
531 //			}
532 		break;
533 
534 //		case CONTROL('c'): goto END;
535 
536 //		case ESC: // fallthrough
537 		case 'q': goto END;
538 
539 		case 10: goto PRINT; // Enter
540 		}
541 
542 		// now highlight the next item in the list.
543 		wattron(w, A_STANDOUT);
544 		sprintf(item, "%-7s", list[i]);
545 		mvwprintw(w, (int)i + 1, 2, "%s", item);
546 		wattroff(w, A_STANDOUT);
547 		wrefresh(w);
548 	}
549 
550 END:
551 	delwin(w);
552 	endwin();
553 	printf("\x1b[%dA", lines);
554 	return (-1);
555 
556 PRINT:
557 	delwin(w);
558 	endwin();
559 	// Restore cursor position
560 	printf("\x1b[%d;%dH", currow - lines, curcol);
561 	char *d = escape_str(list[i]);
562 	rl_insert_text(d + strlen(l));
563 	free(d);
564 	return EXIT_SUCCESS;
565 } */
566 
567 /* Display possible completions using FZF. If one of these possible
568  * completions is selected, insert it into the current line buffer */
569 static int
fzftabcomp(char ** matches)570 fzftabcomp(char **matches)
571 {
572 	FILE *fp = fopen(FZFTABIN, "w");
573 	if (!fp) {
574 		_err('e', PRINT_PROMPT, "%s: %s: %s\n", PROGRAM_NAME,
575 			FZFTABIN, strerror(errno));
576 		return EXIT_FAILURE;
577 	}
578 
579 	int exit_status = EXIT_SUCCESS;
580 
581 	/* Store possible completions in FZFTABIN to pass them to FZF */
582 	size_t i;
583 	for (i = 1; matches[i]; i++) {
584 		if (!matches[i] || !*matches[i])
585 			continue;
586 
587 		char *cl = df_c;
588 		char *color = df_c;
589 		char *entry = matches[i];
590 
591 		if (cur_comp_type != TCMP_HIST && cur_comp_type != TCMP_JUMP) {
592 			cl = get_entry_color(matches, i);
593 
594 			char ext_cl[MAX_COLOR + 5];
595 			*ext_cl = '\0';
596 			/* If color does not start with escape, then we have a color
597 			 * for a file extension. In this case, we need to properly
598 			 * construct the color code */
599 			if (cl && *cl != _ESC)
600 				snprintf(ext_cl, MAX_COLOR + 4, "\x1b[%sm", cl);
601 
602 			char *p = (char *)NULL;
603 			if (cur_comp_type != TCMP_SEL && cur_comp_type != TCMP_DESEL
604 			&& cur_comp_type != TCMP_OPENWITH)
605 				p = strrchr(matches[i], '/');
606 			color = *ext_cl ? ext_cl : (cl ? cl : "");
607 			entry = (p && *(++p)) ? p : matches[i];
608 		}
609 
610 		if (*entry && !SELFORPARENT(entry))
611 			fprintf(fp, "%s%s%s\n", color, entry, df_c);
612 	}
613 
614 	fclose(fp);
615 
616 	/* Set a pointer to the last word (either space or slash) in the
617 	 * input buffer. We use this to highlight the matching prefix in FZF */
618 	char *lw = get_last_word(matches[0]);
619 
620 	/* Calculate the height of the FZF window based on the amount
621 	 * of entries */
622 	size_t height = 0;
623 	if ((int)i + 1 > term_rows - 2)
624 		height = term_rows - 2;
625 	else
626 		height = i;
627 
628 	/* Calculate the offset (left padding) of the FZF window based on
629 	 * cursor position and current query string */
630 	int max_fzf_offset = term_cols > 20 ? term_cols - 20 : 0;
631 	int fzf_offset = (rl_point + prompt_offset < max_fzf_offset)
632 			? (rl_point + prompt_offset - 4) : 0;
633 
634 	if (!lw)
635 		fzf_offset++;
636 	else
637 		fzf_offset -= (int)(strlen(lw) - 1);
638 
639 	char *query = (char *)NULL;
640 	switch(cur_comp_type) {
641 	case TCMP_SEL: break;
642 	case TCMP_DESEL: {
643 		char *sp = strrchr(rl_line_buffer, ' ');
644 		query = (sp && *(++sp)) ? sp : rl_line_buffer;
645 		}
646 		break;
647 
648 	case TCMP_RANGES: break;
649 
650 	case TCMP_HIST:
651 		/* Skip the leading ! char of the input string */
652 		query = rl_line_buffer + 1;
653 		fzf_offset = 1 + prompt_offset - 3;
654 		break;
655 
656 	case TCMP_JUMP: {
657 		char *sp = strchr(rl_line_buffer, ' ');
658 		if (sp && *(++sp)) {
659 			query = sp;
660 			if (*(rl_line_buffer + 1) == ' ')
661 				/* The command is "j" */
662 				fzf_offset = 2 + prompt_offset - 3;
663 			else
664 				/* The command is "jump" */
665 				fzf_offset = 5 + prompt_offset - 3;
666 		} else {
667 			query = rl_line_buffer;
668 		}
669 		}
670 		break;
671 
672 	default: query = lw;
673 	}
674 
675 	if (fzf_offset < 0)
676 		fzf_offset = 0;
677 
678 	/* Run FZF and store the ouput into the FZFTABOUT file */
679 	int ret = run_fzf(&height, &fzf_offset, query);
680 	unlink(FZFTABIN);
681 
682 	/* Calculate currently used lines to go back to the correct cursor
683 	 * position after quitting FZF */
684 	int lines = 1, total_line_len = 0;
685 	total_line_len = rl_end + prompt_offset;
686 	/* PROMPT_OFFSET (the space used by the prompt in the current line)
687 	 * is calculated the first time we print the prompt (in my_rl_getc
688 	 * (readline.c)) */
689 
690 	if (total_line_len > term_cols) {
691 		lines = total_line_len / term_cols;
692 		int rem = (int)total_line_len % term_cols;
693 		if (rem > 0)
694 			lines++;
695 	}
696 
697 	printf("\x1b[%dA", lines);
698 
699 	/* No results */
700 	if (ret != EXIT_SUCCESS)
701 		return EXIT_FAILURE;
702 
703 	fp = fopen(FZFTABOUT, "r");
704 	if (!fp) {
705 		_err('e', PRINT_PROMPT, "%s: %s: %s\n", PROGRAM_NAME,
706 			FZFTABOUT, strerror(errno));
707 		return EXIT_FAILURE;
708 	}
709 
710 	/* Recover FZF ouput */
711 	/* This should be enough space for a file name and the chars taken by the
712 	 * command itself */
713 	char buf[PATH_MAX + NAME_MAX];
714 	fgets(buf, sizeof(buf), fp);
715 	fclose(fp);
716 	unlink(FZFTABOUT);
717 
718 	/* Calculate the length of the matching prefix to insert into the
719 	 * line buffer only the non-matched part of the string returned
720 	 * by FZF */
721 	size_t offset = 0, mlen = strlen(matches[0]);
722 	if (mlen && matches[0][mlen - 1] != '/') {
723 		char *q = strrchr(matches[0], '/');
724 		if (q)
725 			offset = strlen(q + 1);
726 		else
727 			offset = mlen;
728 	}
729 
730 	if (cur_comp_type == TCMP_OPENWITH) {
731 		/* Interpret the corresponding cmd line in the mimelist file
732 		 * and replace the input string by the interpreted line */
733 		char *sp = strchr(rl_line_buffer, ' ');
734 		if (!sp || !*(sp++))
735 			return EXIT_FAILURE;
736 
737 		char *t = sp;
738 		while (*t) {
739 			if (t != sp && *t == ' ' && *(t - 1) != '\\') {
740 				*t = '\0';
741 				break;
742 			}
743 			++t;
744 		}
745 
746 		size_t splen = (size_t)(t - sp);
747 		if (sp[splen - 1] == '/')
748 			sp[--splen] = '\0';
749 
750 		rl_delete_text(0, rl_end);
751 		rl_point = rl_end = 0;
752 		offset = 0;
753 
754 		t = strchr(buf, '%');
755 		if (t && *(t + 1) == 'f') {
756 			char *ss = replace_substr(buf, "%f", sp);
757 			if (ss) {
758 				xstrsncpy(buf, ss, sizeof(buf));
759 				free(ss);
760 			}
761 		} else {
762 			size_t blen = strlen(buf);
763 			if (buf[blen - 1] == '\n')
764 				buf[--blen] = '\0';
765 			snprintf(buf + blen, sizeof(buf) - blen, " %s", sp);
766 		}
767 
768 	} else if (cur_comp_type == TCMP_DESEL) {
769 		offset = strlen(query);
770 
771 	} else if (cur_comp_type == TCMP_HIST || cur_comp_type == TCMP_JUMP) {
772 		rl_delete_text(0, rl_end);
773 		rl_point = rl_end = 0;
774 		offset = 0;
775 
776 	} else if (cur_comp_type == TCMP_RANGES || cur_comp_type == TCMP_SEL) {
777 		char *s = strrchr(rl_line_buffer, ' ');
778 		if (s) {
779 			rl_point = (int)(s - rl_line_buffer + 1);
780 			rl_delete_text(rl_point, rl_end);
781 			rl_end = rl_point;
782 			offset = 0;
783 		}
784 
785 	} else if (!case_sens_path_comp && query) {
786 		/* Honor case insensitive completion */
787 		size_t query_len = strlen(query);
788 		if (strncmp(query, buf, query_len) != 0) {
789 			int bk = rl_point;
790 			rl_delete_text(bk - (int)query_len, rl_end);
791 			rl_point = rl_end = bk - (int)query_len;
792 			offset = 0;
793 		}
794 	}
795 
796 	if (*buf) {
797 		/* Some buffer clean up: remove new line char and ending spaces */
798 		size_t blen = strlen(buf);
799 		int j = (int)blen;
800 		if (buf[j - 1] == '\n')
801 			buf[--j] = '\0';
802 		while (buf[--j] == ' ')
803 			buf[j] = '\0';
804 
805 		char *q = (char *)NULL;
806 		if (cur_comp_type != TCMP_OPENWITH && cur_comp_type != TCMP_PATH) {
807 			q = escape_str(buf);
808 			if (!q)
809 				return EXIT_FAILURE;
810 		} else {
811 			q = savestring(buf, blen);
812 		}
813 
814 		write_completion(q, &offset, &exit_status);
815 		free(q);
816 	}
817 
818 	return exit_status;
819 }
820 #endif /* !_NO_FZF */
821 
822 /* Complete the word at or before point.
823    WHAT_TO_DO says what to do with the completion.
824    `?' means list the possible completions.
825    TAB means do standard completion.
826    `*' means insert all of the possible completions.
827    `!' means to do standard completion, and list all possible completions
828    if there is more than one. */
829 /* This function is taken from an old bash release (1.14.7) and modified
830  * to fit our needs */
831 int
tab_complete(int what_to_do)832 tab_complete(int what_to_do)
833 {
834 	if (rl_notab)
835 		return EXIT_SUCCESS;
836 
837 	rl_compentry_func_t *our_func = (rl_compentry_func_t *)NULL;
838 
839 /*	char *saved_line_buffer = (char *)NULL;
840 	if (rl_line_buffer)
841 		saved_line_buffer = savestring(rl_line_buffer, (size_t)rl_end); */
842 
843 	our_func = rl_completion_entry_function;
844 
845 	/* Only the completion entry function can change these. */
846 	rl_filename_completion_desired = 0;
847 	rl_filename_quoting_desired = 1;
848 
849 	int end = rl_point, delimiter = 0;
850 	char quote_char = '\0';
851 
852 	/* We now look backwards for the start of a filename/variable word. */
853 	if (rl_point) {
854 		int scan = 0;
855 
856 		if (rl_completer_quote_characters) {
857 		/* We have a list of characters which can be used in pairs to
858 		 * quote substrings for the completer. Try to find the start
859 		 * of an unclosed quoted substring. */
860 		/* FOUND_QUOTE is set so we know what kind of quotes we found. */
861 			int pass_next; //found_quote = 0;
862 			for (scan = pass_next = 0; scan < end; scan++) {
863 				if (pass_next) {
864 					pass_next = 0;
865 					continue;
866 				}
867 
868 				if (rl_line_buffer[scan] == '\\') {
869 					pass_next = 1;
870 //					found_quote |= 4;
871 					continue;
872 				}
873 
874 				if (quote_char != '\0') {
875 				/* Ignore everything until the matching close quote char. */
876 					if (rl_line_buffer[scan] == quote_char) {
877 					/* Found matching close.  Abandon this substring. */
878 						quote_char = '\0';
879 						rl_point = end;
880 					}
881 				} else if (strchr(rl_completer_quote_characters,
882 						rl_line_buffer[scan])) {
883 					/* Found start of a quoted substring. */
884 					quote_char = rl_line_buffer[scan];
885 					rl_point = scan + 1;
886 					/* Shell-like quoting conventions. */
887 /*					if (quote_char == '\'')
888 						found_quote |= 1;
889 					else if (quote_char == '"')
890 						found_quote |= 2; */
891 				}
892 			}
893 		}
894 
895 		if (rl_point == end && quote_char == '\0') {
896 		/* We didn't find an unclosed quoted substring upon which to do
897 		 * completion, so use the word break characters to find the
898 		 * substring on which to complete. */
899 			while (--rl_point) {
900 				scan = rl_line_buffer[rl_point];
901 
902 				if (strchr(rl_completer_word_break_characters, scan) == 0
903 				|| (scan == ' '
904 				&& rl_point && rl_line_buffer[rl_point - 1] == '\\'))
905 					continue;
906 
907 				/* Convoluted code, but it avoids an n^2 algorithm with
908 				 * calls to char_is_quoted. */
909 				break;
910 			}
911 		}
912 
913 		/* If we are at an unquoted word break, then advance past it. */
914 		scan = rl_line_buffer[rl_point];
915 		if (strchr(rl_completer_word_break_characters, scan)) {
916 		/* If the character that caused the word break was a quoting
917 		 * character, then remember it as the delimiter. */
918 			if (strchr("\"'", scan) && (end - rl_point) > 1)
919 				delimiter = scan;
920 
921 		/* If the character isn't needed to determine something special
922 		 * about what kind of completion to perform, then advance past it. */
923 			if (!rl_special_prefixes || strchr(rl_special_prefixes, scan) == 0)
924 				rl_point++;
925 		}
926 	}
927 
928 	int start = rl_point;
929 	rl_point = end;
930 	char *text = rl_copy_text(start, end);
931 	char **matches = (char **)NULL;
932 
933 	/* At this point, we know we have an open quote if quote_char != '\0'. */
934 
935   /* If the user wants to TRY to complete, but then wants to give
936    * up and use the default completion function, they set the
937    * variable rl_attempted_completion_function. */
938 	if (rl_attempted_completion_function) {
939 		matches = (*rl_attempted_completion_function) (text, start, end);
940 
941 		if (matches || rl_attempted_completion_over) {
942 			rl_attempted_completion_over = 0;
943 			our_func = (rl_compentry_func_t *)NULL;
944 			goto AFTER_USUAL_COMPLETION;
945 		}
946 	}
947 
948 	matches = rl_completion_matches(text, our_func);
949 
950 AFTER_USUAL_COMPLETION:
951 	free(text);
952 
953 	if (!matches || !matches[0]) {
954 //		rl_ding();
955 		rl_ring_bell();
956 		return EXIT_FAILURE;
957 	}
958 
959 	register size_t i;
960 	int should_quote;
961 
962 	/* It seems to me that in all the cases we handle we would like
963 	 * to ignore duplicate possiblilities. Scan for the text to
964 	 * insert being identical to the other completions. */
965 	if (rl_ignore_completion_duplicates) {
966 		char *lowest_common;
967 		size_t j;
968 		size_t newlen = 0;
969 		char dead_slot;
970 		char **temp_array;
971 
972 		/* Sort the items. */
973 		/* It is safe to sort this array, because the lowest common
974 		denominator found in matches[0] will remain in place. */
975 //			for (i = 0; matches[i]; i++);
976 		/* Try sorting the array without matches[0], since we need it to
977 		stay in place no matter what. */
978 //			if (i)
979 //				qsort(matches + 1, i - 1, sizeof (char *), compare_strings);
980 
981 		/* Remember the lowest common denominator for it may be unique. */
982 		lowest_common = savestring(matches[0], strlen(matches[0]));
983 
984 		for (i = 0; matches[i + 1]; i++) {
985 			if (strcmp(matches[i], matches[i + 1]) == 0) {
986 				free(matches[i]);
987 				matches[i] = (char *)&dead_slot;
988 			} else {
989 				newlen++;
990 			}
991 		}
992 
993 		/* We have marked all the dead slots with (char *)&dead_slot.
994 		 * Copy all the non-dead entries into a new array. */
995 		temp_array = (char **)xnmalloc(3 + newlen, sizeof (char *));
996 		for (i = j = 1; matches[i]; i++) {
997 			if (matches[i] != (char *)&dead_slot)
998 				temp_array[j++] = matches[i];
999 		}
1000 		temp_array[j] = (char *)NULL;
1001 
1002 		if (matches[0] != (char *)&dead_slot)
1003 			free(matches[0]);
1004 		free(matches);
1005 
1006 		matches = temp_array;
1007 
1008 		/* Place the lowest common denominator back in [0]. */
1009 		matches[0] = lowest_common;
1010 
1011 		/* If there is one string left, and it is identical to the
1012 		 * lowest common denominator, then the LCD is the string to
1013 		 * insert. */
1014 		if (j == 2 && strcmp(matches[0], matches[1]) == 0) {
1015 			free(matches[1]);
1016 			matches[1] = (char *)NULL;
1017 		}
1018 	}
1019 
1020 	switch (what_to_do) {
1021 //		case TAB:
1022 	case '!':
1023 		/* If we are matching filenames, then here is our chance to
1024 		 * do clever processing by re-examining the list.  Call the
1025 		 * ignore function with the array as a parameter.  It can
1026 		 * munge the array, deleting matches as it desires. */
1027 		if (rl_ignore_some_completions_function
1028 		&& our_func == rl_completion_entry_function) {
1029 			(void)(*rl_ignore_some_completions_function)(matches);
1030 			if (matches == 0 || matches[0] == 0) {
1031 				if (matches)
1032 					free(matches);
1033 				rl_ding();
1034 				return 0;
1035 			}
1036 		}
1037 
1038 		/* If we are doing completion on quoted substrings, and any matches
1039 		 * contain any of the completer_word_break_characters, then
1040 		 * automatically prepend the substring with a quote character
1041 		 * (just pick the first one from the list of such) if it does not
1042 		 * already begin with a quote string.  FIXME: Need to remove any such
1043 		 * automatically inserted quote character when it no longer is necessary,
1044 		 * such as if we change the string we are completing on and the new
1045 		 * set of matches don't require a quoted substring. */
1046 		char *replacement = matches[0];
1047 
1048 		should_quote = matches[0] && rl_completer_quote_characters &&
1049 		rl_filename_completion_desired && rl_filename_quoting_desired;
1050 
1051 		if (should_quote)
1052 			should_quote = should_quote && !quote_char;
1053 
1054 		if (should_quote) {
1055 			int do_replace;
1056 
1057 			do_replace = NO_MATCH;
1058 
1059 			/* If there is a single match, see if we need to quote it.
1060 			This also checks whether the common prefix of several
1061 			matches needs to be quoted.  If the common prefix should
1062 			not be checked, add !matches[1] to the if clause. */
1063 			should_quote = rl_strpbrk(matches[0],
1064 						   rl_completer_word_break_characters) != 0;
1065 
1066 			if (should_quote)
1067 				do_replace = matches[1] ? MULT_MATCH : SINGLE_MATCH;
1068 
1069 			if (do_replace != NO_MATCH) {
1070 				/* Found an embedded word break character in a potential
1071 				 match, so we need to prepend a quote character if we
1072 				 are replacing the completion string. */
1073 				replacement = escape_str(matches[0]);
1074 			}
1075 		}
1076 
1077 		if (replacement && cur_comp_type != TCMP_HIST
1078 		&& cur_comp_type != TCMP_JUMP && cur_comp_type != TCMP_RANGES
1079 		&& (cur_comp_type != TCMP_SEL || !fzftab || sel_n == 1)) {
1080 			if ((cur_comp_type == TCMP_SEL || cur_comp_type == TCMP_DESEL
1081 			|| cur_comp_type == TCMP_NET) && !strchr(replacement, '\\')) {
1082 				char *r = escape_str(replacement);
1083 				if (!r) {
1084 					if (replacement != matches[0])
1085 						free(replacement);
1086 					break;
1087 				}
1088 				if (replacement != matches[0])
1089 					free(replacement);
1090 				replacement = r;
1091 			}
1092 
1093 			rl_begin_undo_group();
1094 			rl_delete_text(start, rl_point);
1095 			rl_point = start;
1096 #ifndef _NO_HIGHLIGHT
1097 			if (highlight && !wrong_cmd) {
1098 				size_t k, l = 0;
1099 				char *cc = cur_color;
1100 				fputs("\x1b[?25l", stdout);
1101 				char t[PATH_MAX];
1102 				for (k = 0; replacement[k]; k++) {
1103 					rl_highlight(replacement, k, SET_COLOR);
1104 					if (replacement[k] < 0) {
1105 						t[l++] = replacement[k];
1106 						if (replacement[k + 1] >= 0) {
1107 							t[l] = '\0';
1108 							l = 0;
1109 							rl_insert_text(t);
1110 							rl_redisplay();
1111 						}
1112 						continue;
1113 					}
1114 					t[0] = (char)replacement[k];
1115 					t[1] = '\0';
1116 					rl_insert_text(t);
1117 					rl_redisplay();
1118 				}
1119 				fputs("\x1b[?25h", stdout);
1120 				cur_color = cc;
1121 				if (cur_color)
1122 					fputs(cur_color, stdout);
1123 			} else {
1124 				rl_insert_text(replacement);
1125 			}
1126 #else
1127 			rl_insert_text(replacement);
1128 #endif
1129 			rl_end_undo_group();
1130 
1131 			if (replacement != matches[0])
1132 				free(replacement);
1133 		}
1134 
1135 		/* If there are more matches, ring the bell to indicate.
1136 		 If this was the only match, and we are hacking files,
1137 		 check the file to see if it was a directory.  If so,
1138 		 add a '/' to the name.  If not, and we are at the end
1139 		 of the line, then add a space. */
1140 		if (matches[1]) {
1141 			if (what_to_do == '!')
1142 				goto DISPLAY_MATCHES;		/* XXX */
1143 			else if (rl_editing_mode != 0) /* vi_mode */
1144 				rl_ding();	/* There are other matches remaining. */
1145 		} else {
1146 			char temp_string[4];
1147 			int temp_string_index = 0;
1148 
1149 			if (quote_char)
1150 				temp_string[temp_string_index++] = quote_char;
1151 
1152 			temp_string[temp_string_index++] = (char)(delimiter
1153 											? delimiter : ' ');
1154 			temp_string[temp_string_index++] = '\0';
1155 
1156 			if (rl_filename_completion_desired) {
1157 				struct stat finfo;
1158 				char *filename = tilde_expand(matches[0]);
1159 
1160 				if ((stat(filename, &finfo) == 0) && S_ISDIR(finfo.st_mode)) {
1161 					if (rl_line_buffer[rl_point] != '/') {
1162 #ifndef _NO_HIGHLIGHT
1163 						if (highlight && !wrong_cmd) {
1164 							char *cc = cur_color;
1165 							fputs(hd_c, stdout);
1166 							rl_insert_text ("/");
1167 							rl_redisplay();
1168 							fputs(cc, stdout);
1169 						} else {
1170 							rl_insert_text ("/");
1171 						}
1172 #else
1173 						rl_insert_text ("/");
1174 #endif
1175 					}
1176 				} else {
1177 					if (rl_point == rl_end)
1178 						rl_insert_text(temp_string);
1179 				}
1180 				free(filename);
1181 			} else {
1182 				if (rl_point == rl_end)
1183 					rl_insert_text(temp_string);
1184 			}
1185 		}
1186 	break;
1187 
1188 /*		case '*': {
1189 		i = 1;
1190 
1191 		rl_begin_undo_group();
1192 		rl_delete_text(start, rl_point);
1193 		rl_point = start;
1194 		if (matches[1]) {
1195 			while (matches[i]) {
1196 				rl_insert_text(matches[i++]);
1197 				rl_insert_text(" ");
1198 			}
1199 		} else {
1200 			rl_insert_text(matches[0]);
1201 			rl_insert_text(" ");
1202 		}
1203 		rl_end_undo_group();
1204 	}
1205 	break; */
1206 
1207 	case '?': {
1208 		int len = 0, count = 0, limit = 0, max = 0;
1209 		int j = 0, k = 0, l = 0;
1210 
1211 		/* Handle simple case first.  What if there is only one answer? */
1212 		if (!matches[1]) {
1213 			char *temp;
1214 			temp = printable_part(matches[0]);
1215 			rl_crlf();
1216 			print_filename(temp, matches[0]);
1217 			rl_crlf();
1218 			goto RESTART;
1219 		}
1220 
1221 		/* There is more than one answer.  Find out how many there are,
1222 		and find out what the maximum printed length of a single entry
1223 		is. */
1224 
1225 DISPLAY_MATCHES:
1226 /*#ifndef _NO_SUGGESTIONS
1227 		if (wrong_cmd)
1228 			recover_from_wrong_cmd();
1229 #endif */
1230 
1231 		for (max = 0, i = 1; matches[i]; i++) {
1232 			char *temp;
1233 			size_t name_length;
1234 
1235 			temp = printable_part(matches[i]);
1236 			name_length = strlen(temp);
1237 
1238 			if ((int)name_length > max)
1239 			  max = (int)name_length;
1240 		}
1241 
1242 		len = (int)i - 1;
1243 
1244 		/* If there are many items, then ask the user if she
1245 		   really wants to see them all. */
1246 #ifndef _NO_FZF
1247 		if (!fzftab) {
1248 #endif
1249 		{
1250 			if (len >= rl_completion_query_items) {
1251 				putchar('\n');
1252 #ifndef _NO_HIGHLIGHT
1253 				if (highlight && cur_color != tx_c && !wrong_cmd) {
1254 					cur_color = tx_c;
1255 					fputs(tx_c, stdout);
1256 				}
1257 #endif
1258 //					rl_crlf();
1259 				fprintf(rl_outstream,
1260 					 "Display all %d possibilities? (y or n) ", len);
1261 //					rl_crlf();
1262 				fflush(rl_outstream);
1263 				if (!get_y_or_n()) {
1264 //						rl_crlf();
1265 					goto RESTART;
1266 				}
1267 			}
1268 		}
1269 #ifndef _NO_FZF
1270 		}
1271 #endif
1272 
1273 
1274 #ifndef _NO_FZF
1275 		if (!fzftab) {
1276 #endif
1277 		{
1278 			/* How many items of MAX length can we fit in the screen window? */
1279 			max += 2;
1280 			limit = term_cols / max;
1281 			if (limit != 1 && (limit * max == term_cols))
1282 				limit--;
1283 
1284 			/* Avoid a possible floating exception.  If max > screenwidth,
1285 			   limit will be 0 and a divide-by-zero fault will result. */
1286 			if (limit == 0)
1287 			  limit = 1;
1288 
1289 			/* How many iterations of the printing loop? */
1290 			count = (len + (limit - 1)) / limit;
1291 		}
1292 #ifndef _NO_FZF
1293 	}
1294 #endif
1295 
1296 		/* Watch out for special case.  If LEN is less than LIMIT, then
1297 		   just do the inner printing loop.
1298 		   0 < len <= limit  implies  count = 1. */
1299 
1300 		/* Sort the items if they are not already sorted. */
1301 //			if (!rl_ignore_completion_duplicates)
1302 //				qsort(matches + 1, len ? (size_t)len - 1 : 0, sizeof (char *), compare_strings);
1303 
1304 		/* Print the sorted items, up-and-down alphabetically, like
1305 		   ls might. */
1306 //			rl_crlf();
1307 		putchar('\n');
1308 #ifndef _NO_HIGHLIGHT
1309 		if (highlight && cur_color != tx_c && !wrong_cmd) {
1310 			cur_color = tx_c;
1311 			fputs(tx_c, stdout);
1312 		}
1313 #endif
1314 		char *qq = (char *)NULL;
1315 		if (cur_comp_type != TCMP_PATH)
1316 			goto CALC_OFFSET;
1317 
1318 		if (*matches[0] == '~') {
1319 			char *exp_path = tilde_expand(matches[0]);
1320 			if (exp_path) {
1321 				xchdir(exp_path, NO_TITLE);
1322 				free(exp_path);
1323 			}
1324 		} else {
1325 			char *p = strrchr(matches[0], '/');
1326 			if (!p)
1327 				goto CALC_OFFSET;
1328 
1329 			if (p == matches[0]) {
1330 				if (*(p + 1)) {
1331 					char pp = *(p + 1);
1332 					*(p + 1) = '\0';
1333 					xchdir(matches[0], NO_TITLE);
1334 					*(p + 1) = pp;
1335 				} else {
1336 					/* We have the root dir */
1337 					xchdir(matches[0], NO_TITLE);
1338 				}
1339 			} else {
1340 				*p = '\0';
1341 				xchdir(matches[0], NO_TITLE);
1342 				*p = '/';
1343 			}
1344 		}
1345 
1346 CALC_OFFSET:
1347 		qq = strrchr(matches[0], '/');
1348 		if (qq) {
1349 			if (*(++qq)) {
1350 				tab_offset = strlen(qq);
1351 			} else if (cur_comp_type == TCMP_DESEL) {
1352 				tab_offset = strlen(matches[0]);
1353 				qq = matches[0];
1354 			}
1355 		} else {
1356 			tab_offset = strlen(matches[0]);
1357 		}
1358 		if (cur_comp_type == TCMP_RANGES)
1359 			tab_offset = 0;
1360 
1361 /*		if (curses_tab(matches) == -1)
1362 			goto RESTART;
1363 		goto RESET_PATH; */
1364 
1365 #ifndef _NO_FZF
1366 		if (fzftab == 1) {
1367 			if (fzftabcomp(matches) == -1)
1368 				goto RESTART;
1369 			goto RESET_PATH;
1370 		}
1371 #endif
1372 
1373 		for (i = 1; i <= (size_t)count; i++) {
1374 			if (i >= term_rows) {
1375 				/* A little pager */
1376 				fputs("\x1b[7;97m--Mas--\x1b[0;49m", stdout);
1377 				int c = 0;
1378 				while ((c = xgetchar()) == _ESC);
1379 				if (c == 'q') {
1380 					/* Delete the --Mas-- label */
1381 					fputs("\x1b[7D\x1b[7X\x1b[1A\n", stdout);
1382 					break;
1383 				}
1384 				fputs("\x1b[7D\x1b[0K", stdout);
1385 			}
1386 
1387 			for (j = 0, l = (int)i; j < limit; j++) {
1388 				if (l > len || !matches[l]) {
1389 					break;
1390 				} else {
1391 					if (tab_offset) {
1392 						/* Print the matching part of the match */
1393 						printf("\x1b[0m%s%s\x1b[0m%s", ts_c, qq ? qq : matches[0],
1394 						(cur_comp_type == TCMP_CMD) ? (colorize
1395 						? ex_c : "") : dc_c);
1396 					}
1397 
1398 					/* Now print the non-matching part of the match */
1399 					char *temp;
1400 					int printed_length;
1401 					temp = printable_part(matches[l]);
1402 					printed_length = (int)strlen(temp);
1403 					printed_length += print_filename(temp, matches[l]);
1404 
1405 					if (j + 1 < limit) {
1406 						for (k = 0; k < max - printed_length; k++)
1407 							putc(' ', rl_outstream);
1408 					}
1409 				}
1410 				l += count;
1411 			}
1412 			putchar('\n');
1413 //				rl_crlf();
1414 		}
1415 		tab_offset = 0;
1416 
1417 		if (!wrong_cmd && colorize && cur_comp_type == TCMP_CMD)
1418 			fputs(tx_c, stdout);
1419 
1420 #ifndef _NO_FZF
1421 RESET_PATH:
1422 #endif
1423 		if (cur_comp_type == TCMP_PATH)
1424 			xchdir(ws[cur_ws].path, NO_TITLE);
1425 
1426 RESTART:
1427 		rl_on_new_line();
1428 #ifndef _NO_HIGHLIGHT
1429 		if (highlight && !wrong_cmd) {
1430 			int bk = rl_point;
1431 			fputs("\x1b[?25l", stdout);
1432 			char *ss = rl_copy_text(0, rl_end);
1433 			rl_delete_text(0, rl_end);
1434 			rl_redisplay();
1435 			rl_point = rl_end = 0;
1436 			int wc = wrong_cmd_line;
1437 			if (wc) {
1438 				cur_color = hw_c;
1439 				fputs(cur_color, stdout);
1440 			}
1441 			l = 0;
1442 			char t[PATH_MAX];
1443 			for (k = 0; ss[k]; k++) {
1444 				if (ss[k] == ' ')
1445 					wc = 0;
1446 
1447 				if (!wc)
1448 					rl_highlight(ss, (size_t)k, SET_COLOR);
1449 
1450 				if (ss[k] < 0) {
1451 					t[l++] = ss[k];
1452 					if (ss[k + 1] >= 0) {
1453 						t[l] = '\0';
1454 						l = 0;
1455 						rl_insert_text(t);
1456 						rl_redisplay();
1457 					}
1458 					continue;
1459 				}
1460 
1461 				t[0] = (char)ss[k];
1462 				t[1] = '\0';
1463 				rl_insert_text(t);
1464 				rl_redisplay();
1465 			}
1466 			fputs("\x1b[?25h", stdout);
1467 			rl_point = rl_end = bk;
1468 			free(ss);
1469 		}
1470 #endif
1471 		}
1472 		break;
1473 
1474 	default:
1475 		fprintf(stderr, "\r\nreadline: bad value for what_to_do in rl_complete\n");
1476 		abort();
1477 	}
1478 
1479 	for (i = 0; matches[i]; i++)
1480 		free(matches[i]);
1481 	free(matches);
1482 
1483   /* Check to see if the line has changed through all of this manipulation. */
1484 //	if (saved_line_buffer) {
1485 /*		if (strcmp (rl_line_buffer, saved_line_buffer) != 0)
1486 			completion_changed_buffer = 1;
1487 		else
1488 			completion_changed_buffer = 0; */
1489 //		free(saved_line_buffer);
1490 //	}
1491 	return EXIT_SUCCESS;
1492 }
1493