1 /* history.c -- functions for the history system */
2 
3 /*
4  * This file is part of CliFM
5  *
6  * Copyright (C) 2016-2021, L. Abramovich <johndoe.arch@outlook.com>
7  * All rights reserved.
8 
9  * CliFM is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * CliFM is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22  * MA 02110-1301, USA.
23 */
24 
25 #include "helpers.h"
26 
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #include <readline/history.h>
32 
33 #include "aux.h"
34 #include "checks.h"
35 #include "exec.h"
36 #include "history.h"
37 #include "init.h"
38 #include "misc.h"
39 #include "messages.h"
40 #include "file_operations.h"
41 
42 /* Log COMM into LOG_FILE (global) */
43 int
log_function(char ** comm)44 log_function(char **comm)
45 {
46 	/* If cmd logs are disabled, allow only "log" commands */
47 	if (!logs_enabled) {
48 		if (comm && comm[0] && strcmp(comm[0], "log") != 0)
49 			return EXIT_SUCCESS;
50 	}
51 
52 	if (!config_ok)
53 		return EXIT_FAILURE;
54 
55 	int clear_log = 0;
56 
57 	/* If the command was just 'log' */
58 	if (comm && comm[0] && *comm[0] == 'l' && strcmp(comm[0], "log") == 0 && !comm[1]) {
59 		FILE *log_fp;
60 		log_fp = fopen(log_file, "r");
61 		if (!log_fp) {
62 			_err(0, NOPRINT_PROMPT, "%s: log: '%s': %s\n",
63 			    PROGRAM_NAME, log_file, strerror(errno));
64 			return EXIT_FAILURE;
65 		} else {
66 			size_t line_size = 0;
67 			char *line_buff = (char *)NULL;
68 
69 			while (getline(&line_buff, &line_size, log_fp) > 0)
70 				fputs(line_buff, stdout);
71 
72 			free(line_buff);
73 			fclose(log_fp);
74 			return EXIT_SUCCESS;
75 		}
76 	}
77 
78 	else if (comm && comm[0] && *comm[0] == 'l' && strcmp(comm[0], "log") == 0
79 	&& comm[1]) {
80 		if (*comm[1] == 'c' && strcmp(comm[1], "clear") == 0)
81 			clear_log = 1;
82 		else if (*comm[1] == 's' && strcmp(comm[1], "status") == 0) {
83 			printf(_("Logs %s\n"), (logs_enabled) ? _("enabled")
84 							      : _("disabled"));
85 			return EXIT_SUCCESS;
86 		} else if (*comm[1] == 'o' && strcmp(comm[1], "on") == 0) {
87 			if (logs_enabled) {
88 				puts(_("Logs already enabled"));
89 			} else {
90 				logs_enabled = 1;
91 				puts(_("Logs successfully enabled"));
92 			}
93 			return EXIT_SUCCESS;
94 		} else if (*comm[1] == 'o' && strcmp(comm[1], "off") == 0) {
95 			/* If logs were already disabled, just exit. Otherwise, log
96 			 * the "log off" command */
97 			if (!logs_enabled) {
98 				puts(_("Logs already disabled"));
99 				return EXIT_SUCCESS;
100 			} else {
101 				puts(_("Logs succesfully disabled"));
102 				logs_enabled = 0;
103 			}
104 		}
105 	}
106 
107 	/* Construct the log line */
108 	if (!last_cmd) {
109 		if (!logs_enabled) {
110 			/* When cmd logs are disabled, "log clear" and "log off" are
111 			 * the only commands that can reach this code */
112 			if (clear_log) {
113 				last_cmd = (char *)xnmalloc(10, sizeof(char));
114 				strcpy(last_cmd, "log clear");
115 			} else {
116 				last_cmd = (char *)xnmalloc(8, sizeof(char));
117 				strcpy(last_cmd, "log off");
118 			}
119 		} else {
120 		/* last_cmd should never be NULL if logs are enabled (this
121 		 * variable is set immediately after taking valid user input
122 		 * in the prompt function). However ... */
123 			last_cmd = (char *)xnmalloc(23, sizeof(char));
124 			strcpy(last_cmd, _("Error getting command!"));
125 		}
126 	}
127 
128 	char *date = get_date();
129 	size_t log_len = strlen(date) + strlen(ws[cur_ws].path)
130 					+ strlen(last_cmd) + 6;
131 	char *full_log = (char *)xnmalloc(log_len, sizeof(char));
132 
133 	snprintf(full_log, log_len, "[%s] %s:%s\n", date, ws[cur_ws].path, last_cmd);
134 
135 	free(date);
136 	free(last_cmd);
137 	last_cmd = (char *)NULL;
138 
139 	/* Write the log into LOG_FILE */
140 	FILE *log_fp;
141 	/* If not 'log clear', append the log to the existing logs */
142 
143 	if (!clear_log)
144 		log_fp = fopen(log_file, "a");
145 	else
146 	/* Else, overwrite the log file leaving only the 'log clear'
147 	 * command */
148 		log_fp = fopen(log_file, "w+");
149 
150 	if (!log_fp) {
151 		_err('e', PRINT_PROMPT, "%s: log: '%s': %s\n", PROGRAM_NAME,
152 		    log_file, strerror(errno));
153 		free(full_log);
154 		return EXIT_FAILURE;
155 	} else { /* If LOG_FILE was correctly opened, write the log */
156 		fputs(full_log, log_fp);
157 		free(full_log);
158 		fclose(log_fp);
159 
160 		return EXIT_SUCCESS;
161 	}
162 }
163 
164 /* Handle the error message MSG. Store MSG in an array of error
165  * messages, write it into an error log file, and print it immediately
166  * (if PRINT is zero (NOPRINT_PROMPT) or tell the next prompt, if PRINT
167  * is one to do it (PRINT_PROMPT)). Messages wrote to the error log file
168  * have the following format:
169  * "[date] msg", where 'date' is YYYY-MM-DDTHH:MM:SS */
170 void
log_msg(char * _msg,int print)171 log_msg(char *_msg, int print)
172 {
173 	if (!_msg)
174 		return;
175 
176 	size_t msg_len = strlen(_msg);
177 	if (msg_len == 0)
178 		return;
179 
180 	/* Store messages (for current session only) in an array, so that
181 	 * the user can check them via the 'msg' command */
182 	msgs_n++;
183 	messages = (char **)xrealloc(messages, (size_t)(msgs_n + 1) * sizeof(char *));
184 	messages[msgs_n - 1] = savestring(_msg, msg_len);
185 	messages[msgs_n] = (char *)NULL;
186 
187 	if (print) /* PRINT_PROMPT */
188 		/* The next prompt will take care of printing the message */
189 		print_msg = 1;
190 	else /* NOPRINT_PROMPT */
191 		/* Print the message directly here */
192 		fputs(_msg, stderr);
193 
194 	/* If the config dir cannot be found or if msg log file isn't set
195 	 * yet... This will happen if an error occurs before running
196 	 * init_config(), for example, if the user's home cannot be found */
197 	if (!config_ok || !msg_log_file || !*msg_log_file)
198 		return;
199 
200 	FILE *msg_fp = fopen(msg_log_file, "a");
201 	if (!msg_fp) {
202 		/* Do not log this error: We might incur in an infinite loop
203 		 * trying to access a file that cannot be accessed */
204 		fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, msg_log_file,
205 		    strerror(errno));
206 		fputs("Press any key to continue... ", stdout);
207 		xgetchar();
208 		putchar('\n');
209 	} else {
210 		/* Write message to messages file: [date] msg */
211 		time_t rawtime = time(NULL);
212 		struct tm tm;
213 		localtime_r(&rawtime, &tm);
214 		char date[64] = "";
215 
216 		strftime(date, sizeof(date), "%b %d %H:%M:%S %Y", &tm);
217 		fprintf(msg_fp, "[%d-%d-%dT%d:%d:%d] ", tm.tm_year + 1900,
218 		    tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
219 		fputs(_msg, msg_fp);
220 		fclose(msg_fp);
221 	}
222 }
223 
224 int
save_dirhist(void)225 save_dirhist(void)
226 {
227 	if (!dirhist_file)
228 		return EXIT_FAILURE;
229 
230 	if (!old_pwd || !old_pwd[0])
231 		return EXIT_SUCCESS;
232 
233 	FILE *fp = fopen(dirhist_file, "w");
234 	if (!fp) {
235 		fprintf(stderr, _("%s: Could not save directory history: %s\n"),
236 		    PROGRAM_NAME, strerror(errno));
237 		return EXIT_FAILURE;
238 	}
239 
240 	int i;
241 	for (i = 0; i < dirhist_total_index; i++) {
242 		/* Exclude invalid entries */
243 		if (!old_pwd[i] || *old_pwd[i] == _ESC)
244 			continue;
245 		fprintf(fp, "%s\n", old_pwd[i]);
246 	}
247 
248 	fclose(fp);
249 	return EXIT_SUCCESS;
250 }
251 
252 /* Add DIR_PATH to visited directory history (old_pwd) */
253 void
add_to_dirhist(const char * dir_path)254 add_to_dirhist(const char *dir_path)
255 {
256 	/*  static size_t end_counter = 11, mid_counter = 11; */
257 
258 	/* If already at the end of dirhist, add new entry */
259 	if (dirhist_cur_index + 1 >= dirhist_total_index) {
260 		/* Do not add anything if new path equals last entry in
261 		 * directory history */
262 		if ((dirhist_total_index - 1) >= 0 && old_pwd[dirhist_total_index - 1] && *(dir_path + 1) == *(old_pwd[dirhist_total_index - 1] + 1) && strcmp(dir_path, old_pwd[dirhist_total_index - 1]) == 0)
263 			return;
264 
265 		/* Realloc only once per 10 operations */
266 		/*      if (end_counter > 10) {
267 			end_counter = 1; */
268 		/* 20: Realloc dirhist_total + (2 * 10) */
269 		old_pwd = (char **)xrealloc(old_pwd,
270 		    (size_t)(dirhist_total_index + 2) * sizeof(char *));
271 		/*      }
272 
273 		end_counter++; */
274 
275 		dirhist_cur_index = dirhist_total_index;
276 		old_pwd[dirhist_total_index++] = savestring(dir_path, strlen(dir_path));
277 		old_pwd[dirhist_total_index] = (char *)NULL;
278 	}
279 
280 	/* I not at the end of dirhist, add previous AND new entry */
281 	else {
282 		/*      if (mid_counter > 10) {
283 			mid_counter = 1; */
284 		/* 30: Realloc dirhist_total + (3 * 10) */
285 		old_pwd = (char **)xrealloc(old_pwd,
286 		    (size_t)(dirhist_total_index + 3) * sizeof(char *));
287 		/*      }
288 
289 		mid_counter++; */
290 
291 		old_pwd[dirhist_total_index++] = savestring(
292 		    old_pwd[dirhist_cur_index],
293 		    strlen(old_pwd[dirhist_cur_index]));
294 
295 		dirhist_cur_index = dirhist_total_index;
296 		old_pwd[dirhist_total_index++] = savestring(dir_path, strlen(dir_path));
297 
298 		old_pwd[dirhist_total_index] = (char *)NULL;
299 	}
300 }
301 
302 static int
reload_history(char ** comm)303 reload_history(char **comm)
304 {
305 	clear_history();
306 	read_history(hist_file);
307 	history_truncate_file(hist_file, max_hist);
308 
309 	/* Update the history array */
310 	if (get_history() != 0)
311 		return EXIT_FAILURE;
312 
313 	if (log_function(comm) != 0)
314 		return EXIT_FAILURE;
315 
316 	return EXIT_SUCCESS;
317 }
318 
319 int
history_function(char ** comm)320 history_function(char **comm)
321 {
322 	if (!config_ok) {
323 		fprintf(stderr, _("%s: History function disabled\n"), PROGRAM_NAME);
324 		return EXIT_FAILURE;
325 	}
326 
327 	/* If no arguments, print the history list */
328 	if (args_n == 0) {
329 		size_t i;
330 		for (i = 0; i < current_hist_n; i++)
331 			printf("  %zu  %s\n", i + 1, history[i]);
332 		return EXIT_SUCCESS;
333 	}
334 
335 	if (args_n >= 1 && *comm[1] == 'e' && strcmp(comm[1], "edit") == 0) {
336 		struct stat attr;
337 		if (stat(hist_file, &attr) == -1) {
338 			fprintf(stderr, "%s: history: %s: %s\n", PROGRAM_NAME, hist_file,
339 					strerror(errno));
340 			return EXIT_FAILURE;
341 		}
342 		time_t mtime_bfr = (time_t)attr.st_mtime;
343 
344 		int ret = EXIT_SUCCESS;
345 
346 		/* If there is an argument... */
347 		if (comm[2]) {
348 			char *cmd[] = {comm[2], hist_file, NULL};
349 			ret = launch_execve(cmd, FOREGROUND, E_NOSTDERR);
350 		} else {
351 			/* If no application was passed as 2nd argument */
352 			open_in_foreground = 1;
353 			ret = open_file(hist_file);
354 			open_in_foreground = 0;
355 		}
356 
357 		if (ret != EXIT_SUCCESS)
358 			return EXIT_FAILURE;
359 
360 		/* Get modification time after opening the config file */
361 		stat(config_file, &attr);
362 		/* If modification times differ, the file was modified after being
363 		 * opened */
364 		if (mtime_bfr != (time_t)attr.st_mtime)
365 			return reload_history(comm);
366 
367 		return EXIT_SUCCESS;
368 	}
369 
370 	/* If 'history clear', guess what, clear the history list! */
371 	if (args_n == 1 && *comm[1] == 'c' && strcmp(comm[1], "clear") == 0) {
372 		FILE *hist_fp = fopen(hist_file, "w+");
373 		if (!hist_fp) {
374 			_err(0, NOPRINT_PROMPT, "%s: history: %s: %s\n",
375 			    PROGRAM_NAME, hist_file, strerror(errno));
376 			return EXIT_FAILURE;
377 		}
378 
379 		/* Do not create an empty file */
380 		fprintf(hist_fp, "%s %s\n", comm[0], comm[1]);
381 		fclose(hist_fp);
382 
383 		/* Reset readline history */
384 		return reload_history(comm);
385 /*		clear_history();
386 		read_history(hist_file);
387 		history_truncate_file(hist_file, max_hist);
388 
389 		// Update the history array
390 		int exit_status = EXIT_SUCCESS;
391 
392 		if (get_history() != 0)
393 			exit_status = EXIT_FAILURE;
394 
395 		if (log_function(comm) != 0)
396 			exit_code = EXIT_FAILURE; */
397 
398 //		return exit_status;
399 	}
400 
401 	/* If 'history -n', print the last -n elements */
402 	if (args_n == 1 && comm[1][0] == '-' && is_number(comm[1] + 1)) {
403 		int num = atoi(comm[1] + 1);
404 
405 		if (num < 0 || num > (int)current_hist_n)
406 			num = (int)current_hist_n;
407 
408 		size_t i;
409 		for (i = current_hist_n - (size_t)num; i < current_hist_n; i++)
410 			printf("%zu %s\n", i + 1, history[i]);
411 
412 		return EXIT_SUCCESS;
413 	}
414 
415 	/* None of the above */
416 	puts(_(HISTORY_USAGE));
417 	return EXIT_SUCCESS;
418 }
419 
420 static int
exec_hist_cmd(char ** cmd)421 exec_hist_cmd(char **cmd)
422 {
423 	int i;
424 	int exit_status = EXIT_SUCCESS;
425 
426 	char **alias_cmd = check_for_alias(cmd);
427 	if (alias_cmd) {
428 		/* If an alias is found, check_for_alias frees CMD and
429 		 * returns alias_cmd in its place to be executed by
430 		 * exec_cmd() */
431 
432 		if (exec_cmd(alias_cmd) != 0)
433 			exit_status = EXIT_FAILURE;
434 
435 		for (i = 0; alias_cmd[i]; i++)
436 			free(alias_cmd[i]);
437 		free(alias_cmd);
438 		alias_cmd = (char **)NULL;
439 	} else {
440 		if (exec_cmd(cmd) != 0)
441 			exit_status = EXIT_FAILURE;
442 
443 		for (i = 0; cmd[i]; i++)
444 			free(cmd[i]);
445 		free(cmd);
446 	}
447 
448 	return exit_status;
449 }
450 
451 /* Takes as argument the history cmd less the first exclamation mark.
452  * Example: if exec_cmd() gets "!-10" it pass to this function "-10",
453  * that is, comm + 1 */
454 int
run_history_cmd(const char * cmd)455 run_history_cmd(const char *cmd)
456 {
457 	/* If "!n" */
458 	int exit_status = EXIT_SUCCESS;
459 	size_t old_args = args_n;
460 
461 	if (is_number(cmd)) {
462 		int num = atoi(cmd);
463 
464 		if (num <= 0 || num > (int)current_hist_n) {
465 			fprintf(stderr, _("%s: !%d: event not found\n"), PROGRAM_NAME, num);
466 			return EXIT_FAILURE;
467 		}
468 
469 		if (record_cmd(history[num - 1]))
470 			add_to_cmdhist(history[num - 1]);
471 
472 		char **cmd_hist = parse_input_str(history[num - 1]);
473 		if (!cmd_hist) {
474 			fprintf(stderr, _("%s: Error parsing history command\n"),
475 				PROGRAM_NAME);
476 			return EXIT_FAILURE;
477 		}
478 
479 		exit_status = exec_hist_cmd(cmd_hist);
480 		args_n = old_args;
481 		return exit_status;
482 	}
483 
484 	/* If "!!", execute the last command */
485 	if (*cmd == '!' && !cmd[1]) {
486 
487 		if (record_cmd(history[current_hist_n - 1]))
488 			add_to_cmdhist(history[current_hist_n - 1]);
489 
490 		char **cmd_hist = parse_input_str(history[current_hist_n - 1]);
491 		if (!cmd_hist) {
492 			fprintf(stderr, _("%s: Error parsing history command\n"),
493 				PROGRAM_NAME);
494 			return EXIT_FAILURE;
495 		}
496 
497 		exit_status = exec_hist_cmd(cmd_hist);
498 		args_n = old_args;
499 		return exit_status;
500 	}
501 
502 	/* If "!-n" */
503 	if (*cmd == '-') {
504 		/* If not number or zero or bigger than max... */
505 		int acmd = atoi(cmd + 1);
506 
507 		if (!is_number(cmd + 1) || acmd == 0 || acmd > (int)current_hist_n - 1) {
508 			fprintf(stderr, _("%s: !%s: Event not found\n"), PROGRAM_NAME, cmd);
509 			return EXIT_FAILURE;
510 		}
511 
512 		char **cmd_hist = parse_input_str(history[current_hist_n - (size_t)acmd - 1]);
513 		if (cmd_hist) {
514 			exit_status = exec_hist_cmd(cmd_hist);
515 
516 			if (record_cmd(history[current_hist_n - (size_t)acmd - 1]))
517 				add_to_cmdhist(history[current_hist_n - (size_t)acmd - 1]);
518 
519 			args_n = old_args;
520 			return exit_status;
521 		}
522 
523 		if (record_cmd(history[current_hist_n - (size_t)acmd - 1]))
524 			add_to_cmdhist(history[current_hist_n - (size_t)acmd - 1]);
525 
526 		fprintf(stderr, _("%s: Error parsing history command\n"), PROGRAM_NAME);
527 		return EXIT_FAILURE;
528 	}
529 
530 	/* If !STRING */
531 	if ((*cmd >= 'a' && *cmd <= 'z') || (*cmd >= 'A' && *cmd <= 'Z')) {
532 		size_t len = strlen(cmd);
533 		size_t i;
534 		for (i = 0; history[i]; i++) {
535 			if (*cmd == *history[i] && strncmp(cmd, history[i], len) == 0) {
536 				char **cmd_hist = parse_input_str(history[i]);
537 				if (!cmd_hist)
538 					continue;
539 
540 				exit_status = exec_hist_cmd(cmd_hist);
541 				args_n = old_args;
542 				return exit_status;
543 			}
544 		}
545 
546 		fprintf(stderr, _("%s: !%s: Event not found\n"), PROGRAM_NAME, cmd);
547 		return EXIT_FAILURE;
548 	}
549 
550 	puts(_(HISTEXEC_USAGE));
551 	return EXIT_SUCCESS;
552 }
553 
554 int
get_history(void)555 get_history(void)
556 {
557 	if (!config_ok)
558 		return EXIT_FAILURE;
559 
560 	if (current_hist_n == 0) { /* Coming from main() */
561 		history = (char **)xcalloc(1, sizeof(char *));
562 	} else { /* Only true when comming from 'history clear' */
563 		size_t i;
564 		for (i = 0; history[i]; i++)
565 			free(history[i]);
566 		history = (char **)xrealloc(history, 1 * sizeof(char *));
567 		current_hist_n = 0;
568 	}
569 
570 	FILE *hist_fp = fopen(hist_file, "r");
571 	if (!hist_fp) {
572 		_err('e', PRINT_PROMPT, "%s: history: '%s': %s\n",
573 		    PROGRAM_NAME, hist_file, strerror(errno));
574 		return EXIT_FAILURE;
575 	}
576 
577 	size_t line_size = 0;
578 	char *line_buff = (char *)NULL;
579 	ssize_t line_len = 0;
580 
581 	while ((line_len = getline(&line_buff, &line_size, hist_fp)) > 0) {
582 		line_buff[line_len - 1] = '\0';
583 		history = (char **)xrealloc(history, (current_hist_n + 2) * sizeof(char *));
584 		history[current_hist_n++] = savestring(line_buff, (size_t)line_len);
585 	}
586 
587 	curhistindex = current_hist_n ? current_hist_n - 1 : 0;
588 	history[current_hist_n] = (char *)NULL;
589 	free(line_buff);
590 	fclose(hist_fp);
591 	return EXIT_SUCCESS;
592 }
593 
594 void
add_to_cmdhist(const char * cmd)595 add_to_cmdhist(const char *cmd)
596 {
597 	if (!cmd)
598 		return;
599 
600 	/* For readline */
601 	add_history(cmd);
602 
603 	if (config_ok)
604 		append_history(1, hist_file);
605 
606 	/* For us */
607 	/* Add the new input to the history array */
608 	size_t cmd_len = strlen(cmd);
609 	history = (char **)xrealloc(history, (size_t)(current_hist_n + 2) * sizeof(char *));
610 	history[current_hist_n++] = savestring(cmd, cmd_len);
611 	history[current_hist_n] = (char *)NULL;
612 }
613 
614 /* Returns 1 if INPUT should be stored in history and 0 if not */
615 int
record_cmd(char * input)616 record_cmd(char *input)
617 {
618 	/* NULL input */
619 	if (!input || !*input)
620 		return 0;
621 
622 	if (SELFORPARENT(input))
623 		return 0;
624 
625 	/* Blank lines */
626 	unsigned int blank = 1;
627 	char *p = input;
628 
629 	while (*p) {
630 		if (*p > ' ') {
631 			blank = 0;
632 			break;
633 		}
634 		p++;
635 	}
636 
637 	if (blank)
638 		return 0;
639 
640 	/* Rewind the pointer to the beginning of the input line */
641 	p = input;
642 
643 	/* Commands starting with space */
644 	if (*p == ' ')
645 		return 0;
646 
647 	switch (*p) {
648 	/* Do not record single ELN's */
649 	case '0': /* fallthrough */
650 	case '1': /* fallthrough */
651 	case '2': /* fallthrough */
652 	case '3': /* fallthrough */
653 	case '4': /* fallthrough */
654 	case '5': /* fallthrough */
655 	case '6': /* fallthrough */
656 	case '7': /* fallthrough */
657 	case '8': /* fallthrough */
658 	case '9':
659 		if (is_number(p))
660 			return 0;
661 		break;
662 
663 	case '.': /* . */
664 		if (!*(p + 1))
665 			return 0;
666 		break;
667 
668 	/* Do not record the history command itself */
669 	case 'h':
670 		if (*(p + 1) == 'i' && strcmp(p, "history") == 0)
671 			return 0;
672 		break;
673 
674 	case 'r': /* rf command */
675 		if (*(p + 1) == 'f' && !*(p + 2))
676 			return 0;
677 		break;
678 
679 	/* Do not record exit commands */
680 	case 'q':
681 		if (*(p + 1) == '\0' || strcmp(p, "quit") == 0)
682 			return 0;
683 		break;
684 
685 	case 'Q':
686 		if (*(p + 1) == '\0')
687 			return 0;
688 		break;
689 
690 	case 'e':
691 		if (*(p + 1) == 'x' && strcmp(p, "exit") == 0)
692 			return 0;
693 		break;
694 
695 /*	case 'z':
696 		if (*(p + 1) == 'z' && *(p + 2) == '\0')
697 			return 0;
698 		break;
699 
700 	case 's':
701 		if (*(p + 1) == 'a' && strcmp(p, "salir") == 0)
702 			return 0;
703 		break;
704 
705 	case 'c':
706 		if (*(p + 1) == 'h' && strcmp(p, "chau") == 0)
707 			return 0;
708 		break; */
709 
710 	default: break;
711 	}
712 
713 	/* History */
714 	if (*p == '!' && (_ISDIGIT(*(p + 1)) || (*(p + 1) == '-'
715 	&& _ISDIGIT(*(p + 2))) || ((*(p + 1) == '!') && *(p + 2) == '\0')))
716 		return 0;
717 
718 	/* Consequtively equal commands in history */
719 	if (history && history[current_hist_n - 1]
720 	&& *p == *history[current_hist_n - 1]
721 	&& strcmp(p, history[current_hist_n - 1]) == 0)
722 		return 0;
723 
724 	return 1;
725 }
726