1 /*
2  * cmd.c -- read and execute commands, this is the main loop
3  *
4  * Yet Another FTP Client
5  * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version. See COPYING for more details.
11  */
12 
13 #include "syshdr.h"
14 #include "gvars.h"
15 #include "ftp.h"
16 #include "cmd.h"
17 #include "input.h"
18 #include "strq.h"
19 #include "args.h"
20 #include "redir.h"
21 #include "transfer.h"
22 #include "commands.h"
23 #include "alias.h"
24 #include "utils.h"
25 #include "bookmark.h"
26 #include "redir.h"
27 #include "prompt.h"
28 #include "ltag.h"
29 #include "lscolors.h"
30 
31 //static void exe_cmdline(char *str, bool aliases_are_expanded);
32 
exit_yafc(void)33 void exit_yafc(void)
34 {
35 	ftp_quit_all();
36 	list_free(gvFtpList);
37   gvFtpList = NULL;
38 	input_save_history();
39 	save_ltaglist(0);
40 	gvars_destroy();
41 	reset_xterm_title();
42 	free_colors();
43 
44 	exit(0);
45 }
46 
cmd_exit(int argc,char ** argv)47 void cmd_exit(int argc, char **argv)
48 {
49         cmd_quit(argc,argv);
50 }
51 
cmd_quit(int argc,char ** argv)52 void cmd_quit(int argc, char **argv)
53 {
54 	if(argv != 0) {
55 		OPT_HELP_NEW(_("Close all connections and quit."), "quit [options]", NULL);
56 		maxargs(optind - 1);
57 	}
58 	exit_yafc();
59 }
60 
61 /* main loop, prompts for commands and executes them.
62  */
command_loop(void)63 void command_loop(void)
64 {
65 	char *p;
66 #ifdef HAVE_GETTIMEOFDAY
67 	struct timeval beg, end;
68 #endif
69 
70 #ifdef HAVE_POSIX_SIGSETJMP
71 	if(sigsetjmp(gvRestartJmp, 1))
72 #else
73 	if(setjmp(gvRestartJmp))
74 #endif
75 	{
76 		if(!ftp_connected())
77 			printf(_("restarted command loop, connection closed\n"));
78 		else
79 			printf(_("restarted command loop, command aborted\n"));
80 	}
81 	gvJmpBufSet = true;
82 	force_completion_type = cpUnset;
83 	close_redirection();
84 	gvInTransfer = false;
85 	gvInterrupted = false;
86 
87     while(!gvSighupReceived) {
88 		char *cmdstr, *s;
89 
90 		ftp_initsigs();
91 
92 #if 0 && (defined(HAVE_SETPROCTITLE) || defined(linux))
93 		if(gvUseEnvString) {
94 			if(ftp_connected())
95 				setproctitle("%s", ftp->url->hostname);
96 			else
97 				setproctitle(_("not connected"));
98 		}
99 #endif
100 
101 		fputc('\r', stderr);
102 		p = expand_prompt(ftp_connected()
103 						  ? (ftp_loggedin() ? gvPrompt3 : gvPrompt2)
104 						  : gvPrompt1);
105 		if(!p)
106 			p = xstrdup("yafc> ");
107 
108 		print_xterm_title();
109 
110 		cmdstr = input_read_string("%s", p);
111 		free(p);
112 
113 		if(!cmdstr) {  /* bare EOF received */
114 			fputc('\n', stderr);
115 			if(gvQuitOnEOF)
116 				break;
117 			else
118 				continue;
119 		}
120 		s = strip_blanks(cmdstr);
121 		if(!*s) {
122 			/* blank line */
123 			free(cmdstr);
124 			continue;
125 		}
126 #ifdef HAVE_LIBREADLINE
127 		add_history(s);
128 #endif
129 
130 #ifdef HAVE_GETTIMEOFDAY
131 		gettimeofday(&beg, 0);
132 #endif
133 		ftp_trace("yafc: '%s'\n", s);
134 		exe_cmdline(s, false);
135 		free(cmdstr);
136 
137 #ifdef HAVE_GETTIMEOFDAY
138 		gettimeofday(&end, 0);
139 		end.tv_sec -= beg.tv_sec;
140 		if(gvBeepLongCommand && end.tv_sec >= gvLongCommandTime)
141 			fputc('\007', stderr);
142 #endif
143     }
144 	/* end of main loop, exiting program... */
145 	if(gvSighupReceived)
146 		transfer_end_nohup();
147 	else
148 		cmd_quit(0, 0);
149 }
150 
exe_cmd(cmd_t * c,args_t * args)151 static void exe_cmd(cmd_t *c, args_t *args)
152 {
153 	int i;
154 	char *e;
155 
156 	if(!ftp_connected() && c->needconnect)
157 		fprintf(stderr,
158 				_("Not connected. Try 'open --help' for more information.\n"));
159 	else if(!ftp_loggedin() && c->needlogdin)
160 		fprintf(stderr,
161 				_("Not logged in. Try 'user --help' for more information.\n"));
162 	else {
163 		for(i=1; i<args->argc; i++) {
164 			int ret;
165 			switch(args->argv[i][0]) {
166 			  case '|':
167 			  case '>':
168 				e = args_cat2(args, i);
169 				/* remove pipe/redir from command parameters */
170 				args_del(args, i, args->argc);
171 				ret = open_redirection(e); /* modifies stdout and/or stderr */
172 				free(e);
173 				if(ret != 0)
174 					return;
175 				break;
176 			  case '<':
177 				fprintf(stderr, _("input redirection not supported\n"));
178 				return;
179 			}
180 		}
181 
182 		if(c->auto_unquote)
183 			args_unquote(args);
184 		args_remove_empty(args);
185 
186 		if(strcmp(args->argv[0], "shell") == 0
187 		   || strcmp(args->argv[0], "!") == 0)
188 		{
189 			char *e = args_cat(args->argc, args->argv, 1);
190 			bool b = reject_ampersand(e);
191 			free(e);
192 			if(b)
193 				return;
194 		}
195 
196 		gvInterrupted = false;
197 		c->func(args->argc, args->argv);
198 		gvInterrupted = false;
199 		gvInTransfer = false;
200 		close_redirection();
201 		ftp_cache_flush();
202 	}
203 }
204 
expand_alias(const char * cmd)205 static args_t *expand_alias(const char *cmd)
206 {
207 	args_t *args;
208 	alias *a;
209 
210 	a = alias_search(cmd);
211 	if(a == 0 || a == ALIAS_AMBIGUOUS)
212 		return (args_t *)a;
213 
214 	args = args_create();
215 	args_add_args(args, a->value);
216 
217 	return args;
218 }
219 
220 /* executes the commandline in STR
221  * handles expansion of aliases and splits the command line
222  * into ;-separated strings which are fed to exe_cmd()
223  *
224  * modifies STR
225  *
226  * first call should pass aliases_are_expanded == false
227  */
exe_cmdline(char * str,bool aliases_are_expanded)228 void exe_cmdline(char *str, bool aliases_are_expanded)
229 {
230 	char *e;
231 
232 /*	fprintf(stderr, "exe_cmdline: %s\n", str);*/
233 
234 	/* split command into ;-separated commands */
235 	while((e = strqsep(&str, ';')) != 0) {
236 		args_t *args;
237 
238 		/* make an args_t of the command string */
239 		args = args_create();
240 		args_push_back(args, e);
241 
242 		/* remove empty arguments */
243 		args_remove_empty(args);
244 		if(args->argc == 0) {
245 			args_destroy(args);
246 		  continue;
247 		}
248 
249 		rearrange_redirections(args);
250 
251 		if(aliases_are_expanded) {
252 			cmd_t *c;
253 
254 			/* special handling of '!' (synonym for shell command) */
255 			if(args->argv[0][0] == '!' && strlen(args->argv[0]) > 1) {
256 				strpull(args->argv[0], 1);
257 				args_push_front(args, "!");
258 			}
259 
260 			/* now we have the expanded command line in args
261 			 * this string might include multiple real commands (;-separated)
262 			 */
263 
264 			rearrange_redirections(args);
265 
266 			c = find_func(args->argv[0], true);
267 
268 			if(c != 0)
269 				exe_cmd(c, args);
270 			args_destroy(args);
271 		} else {
272 			args_t *expanded_cmd;
273 			char *cmd;
274 			char *xstr;
275 
276 			/* get the command, the first word in the string */
277 			cmd = xstrdup(args->argv[0]);
278 			unquote(cmd);
279 
280 			/* expand command if it's an alias */
281 			expanded_cmd = expand_alias(cmd);
282 
283 			if(expanded_cmd == 0) /* it wasn't an alias */
284 				expanded_cmd = args;
285 			else if(expanded_cmd == (args_t *)ALIAS_AMBIGUOUS) {
286 				fprintf(stderr, _("ambiguous alias '%s'\n"), cmd);
287 				args_destroy(args);
288 				free(cmd);
289 				continue;
290 			} else {
291 				args_t *alias_args;
292 
293 				/* get the arguments for the alias
294 				 * these are used with expand_alias_parameters()
295 				 */
296 				alias_args = args_create();
297 				args_add_args2(alias_args, args, 1);
298 
299 				expand_alias_parameters(&expanded_cmd, alias_args);
300 				args_destroy(alias_args);
301 				args_destroy(args);
302 
303 				/* remove empty arguments */
304 				args_remove_empty(expanded_cmd);
305 				if(expanded_cmd->argc == 0) {
306 					free(cmd);
307 					args_destroy(expanded_cmd);
308 					continue;
309 				}
310 			}
311 
312 			free(cmd);
313 
314 			xstr = args_cat2(expanded_cmd, 0);
315 			exe_cmdline(xstr, true);
316 			free(xstr);
317 			args_destroy(expanded_cmd);
318 		}
319 	}
320 }
321