1 /*
2 * fxp.c -- transfer files between hosts
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 "ftp.h"
15 #include "input.h"
16 #include "transfer.h"
17 #include "gvars.h"
18 #include "commands.h"
19 #include "strq.h"
20 #include "shortpath.h"
21 #include "utils.h"
22
23 #ifdef HAVE_REGEX_H
24 # include <regex.h>
25 #endif
26
27 #define FXP_INTERACTIVE 1
28 #define FXP_APPEND (1 << 1)
29 #define FXP_PRESERVE (1 << 2)
30 #define FXP_PARENTS (1 << 3)
31 #define FXP_RECURSIVE (1 << 4)
32 #define FXP_VERBOSE (1 << 5)
33 #define FXP_FORCE (1 << 6)
34 #define FXP_FORCE_NEWER (1 << 19)
35 #define FXP_NEWER (1 << 7)
36 #define FXP_DELETE_AFTER (1 << 8)
37 #define FXP_UNIQUE (1 << 9)
38 #define FXP_RESUME (1 << 10)
39 #define FXP_TAGGED (1 << 11)
40 #define FXP_NOHUP (1 << 12)
41 #define FXP_SKIP_EXISTING (1 << 13)
42 #define FXP_BACKGROUND (1 << 14)
43 #define FXP_ASCII (1 << 15)
44 #define FXP_BINARY (1 << 16)
45 #define FXP_OUTPUT_FILE (1 << 17)
46 #define FXP_SKIP_EMPTY (1 << 18)
47
48 static Ftp *fxp_target = 0;
49 static bool fxp_batch = false;
50 static bool fxp_owbatch = false;
51 static bool fxp_delbatch = false;
52 static bool fxp_quit = false;
53 static bool fxp_skip_empty = false;
54
55 static char *fxp_glob_mask = 0;
56 static char *fxp_dir_glob_mask = 0;
57 #ifdef HAVE_REGEX
58 static regex_t fxp_rx_mask;
59 static bool fxp_rx_mask_set = false;
60 static regex_t fxp_dir_rx_mask;
61 static bool fxp_dir_rx_mask_set = false;
62 #endif
63
64 /* in get.c */
65 extern int get_sort_func(const void *a, const void *b);
66
print_fxp_syntax(void)67 static void print_fxp_syntax(void)
68 {
69 show_help(_("Transfers files between two remote servers."), "fxp [options] files",
70 _(" -a, --append append if destination exists\n"
71 " -D, --delete-after delete source file after successful transfer\n"
72 " --dir-mask=GLOB enter only directories matching GLOB pattern\n"
73 " --dir-rx-mask=REGEXP\n"
74 " enter only directories matching REGEXP pattern\n"
75 " -f, --force overwrite existing destinations, never prompt\n"
76 " -F, --force-newer do not use cached information with --newer\n"
77 " -e, --skip-empty skip empty files\n"
78 " -H, --nohup transfer files in background (nohup mode), quits yafc\n"
79 " -i, --interactive prompt before each transfer\n"
80 " -L, --logfile=FILE use FILE as logfile instead of ~/.yafc/nohup/nohup.<pid>\n"
81 " -m, --mask=GLOB only transfer files matching GLOB pattern\n"
82 " -M, --rx-mask=REGEXP only transfer files matching REGEXP pattern\n"
83 " -n, --newer transfer file if destination is newer than source file\n"
84 " -o, --output=DEST store in destination file/directory DEST\n"
85 " -p, --preserve try to preserve file attributes\n"
86 " -P, --parents append source path to destination\n"
87 " -q, --quiet overrides --verbose\n"
88 " -r, --recursive transfer directories recursively\n"
89 " -R, --resume resume broken download (restart at eof)\n"
90 " -s, --skip-existing skip file if destination exists\n"
91 " -t, --tagged transfer tagged files\n"
92 " -T, --target=HOST specify target host\n"
93 " --type=TYPE specify transfer type, 'ascii' or 'binary'\n"
94 " -u, --unique store in unique filename (if target supports STOU)\n"
95 " -v, --verbose explain what is being done\n"));
96 }
97
fxp_exclude_func(rfile * f)98 static bool fxp_exclude_func(rfile *f)
99 {
100 if(risdotdir(f))
101 return true;
102 if(fxp_skip_empty == true && !risdir(f) && f->size == 0)
103 return true;
104 return false;
105 }
106
fxp_preserve_attribs(const rfile * fi,char * dest)107 static void fxp_preserve_attribs(const rfile *fi, char *dest)
108 {
109 mode_t m = rfile_getmode(fi);
110 if(m != (mode_t)-1) {
111 if(ftp->has_site_chmod_command)
112 ftp_chmod(dest, get_mode_string(m));
113 }
114 }
115
do_the_fxp(Ftp * srcftp,const char * src,Ftp * destftp,const char * dest,fxpmode_t how,unsigned opt)116 static int do_the_fxp(Ftp *srcftp, const char *src,
117 Ftp *destftp, const char *dest,
118 fxpmode_t how, unsigned opt)
119 {
120 transfer_mode_t type;
121
122 if(test(opt, FXP_NOHUP))
123 fprintf(stderr, "%s\n", src);
124
125 type = ascii_transfer(src) ? tmAscii : gvDefaultType;
126 if(test(opt, FXP_ASCII))
127 type = tmAscii;
128 else if(test(opt, FXP_BINARY))
129 type = tmBinary;
130
131 #if 0 && (defined(HAVE_SETPROCTITLE) || defined(linux))
132 if(gvUseEnvString && ftp_connected())
133 setproctitle("%s, fxp %s", srcftp->url->hostname, src);
134 #endif
135 if(test(opt, FXP_VERBOSE)) {
136 printf("%s\n", src);
137 }
138 const int r = ftp_fxpfile(srcftp, src, destftp, dest, how, type);
139 #if 0 && (defined(HAVE_SETPROCTITLE) || defined(linux))
140 if(gvUseEnvString && ftp_connected())
141 setproctitle("%s", srcftp->url->hostname);
142 #endif
143
144 if(test(opt, FXP_NOHUP)) {
145 if(r == 0)
146 transfer_mail_msg(_("sent %s\n"), src);
147 else
148 transfer_mail_msg(_("failed to send %s: %s\n"),
149 src, ftp_getreply(false));
150 }
151
152 return r;
153 }
154
fxpfile(const rfile * fi,unsigned int opt,const char * output,const char * destname)155 static int fxpfile(const rfile *fi, unsigned int opt,
156 const char *output, const char *destname)
157 {
158 fxpmode_t how = fxpNormal;
159 bool file_exists = false;
160 char *dest, *dpath;
161 bool dir_created;
162 char *dest_dir, *q_dest_dir;
163 Ftp *thisftp = ftp;
164
165 if((fxp_glob_mask
166 && fnmatch(fxp_glob_mask, base_name_ptr(fi->path), 0) == FNM_NOMATCH)
167 #ifdef HAVE_REGEX
168 || (fxp_rx_mask_set
169 && regexec(&fxp_rx_mask,
170 base_name_ptr(fi->path), 0, 0, 0) == REG_NOMATCH)
171 #endif
172 )
173 return 0;
174
175 if(!output)
176 output = ".";
177
178 if(test(opt, FXP_PARENTS)) {
179 char *p = base_dir_xptr(fi->path);
180 if (asprintf(&dest, "%s/%s/%s", output, p, base_name_ptr(fi->path)) == -1)
181 {
182 fprintf(stderr, _("Failed to allocate memory.\n"));
183 free(p);
184 return -1;
185 }
186 free(p);
187 } else if(test(opt, FXP_OUTPUT_FILE))
188 dest = xstrdup(output);
189 else
190 if (asprintf(&dest, "%s/%s", output, base_name_ptr(fi->path)) == -1)
191 {
192 fprintf(stderr, _("Failed to allocate memory.\n"));
193 return -1;
194 }
195
196 path_collapse(dest);
197
198 ftp_use(fxp_target);
199
200 /* make sure destination directory exists */
201 dpath = base_dir_xptr(dest);
202 dest_dir = ftp_path_absolute(dpath);
203 q_dest_dir = backslash_quote(dest_dir);
204 int r = ftp_mkpath(q_dest_dir);
205 free(q_dest_dir);
206 free(dest_dir);
207 if(r == -1) {
208 transfer_mail_msg(_("Couldn't create directory: %s\n"), dest_dir);
209 free(dpath);
210 free(dest);
211 ftp_use(thisftp);
212 return -1;
213 }
214 dir_created = (r == 1);
215
216 if(!dir_created && !test(opt, FXP_UNIQUE) && !test(opt, FXP_FORCE)) {
217 rfile *f;
218 f = ftp_get_file(dest);
219 file_exists = (f != 0);
220 if(f && risdir(f)) {
221 /* can't overwrite a directory */
222 printf(_("%s: is a directory\n"), dest);
223 free(dest);
224 return 0;
225 }
226 }
227
228 if(test(opt, FXP_APPEND)) {
229 how = fxpAppend;
230 } else if(file_exists) {
231 if(test(opt, FXP_SKIP_EXISTING)) {
232 char* sp = shortpath(dest, 42, ftp->homedir);
233 printf(_("Remote file '%s' exists, skipping...\n"), sp);
234 free(sp);
235 free(dest);
236 ftp_use(thisftp);
237 return 0;
238 }
239 else if(test(opt, FXP_NEWER)) {
240 time_t src_ft;
241 time_t dst_ft;
242
243 ftp_use(thisftp);
244 src_ft = ftp_filetime(fi->path, test(opt, FXP_FORCE_NEWER));
245 ftp_use(fxp_target);
246
247 dst_ft = ftp_filetime(dest, test(opt, FXP_FORCE_NEWER));
248
249 if(src_ft != (time_t)-1 && dst_ft != (time_t)-1 && dst_ft >= src_ft) {
250 char* sp = shortpath(dest, 42, ftp->homedir);
251 printf(_("Remote file '%s' is newer than local, skipping...\n"), sp);
252 free(sp);
253 free(dest);
254 ftp_use(thisftp);
255 return 0;
256 }
257 }
258 else if(!test(opt, FXP_RESUME)) {
259 if(!fxp_owbatch) {
260 char* sp = shortpath(dest, 42, ftp->homedir);
261 int a = ask(ASKYES|ASKNO|ASKUNIQUE|ASKCANCEL|ASKALL|ASKRESUME,
262 ASKRESUME,
263 _("File '%s' exists, overwrite?"),
264 sp);
265 free(sp);
266 if(a == ASKCANCEL) {
267 fxp_quit = true;
268 free(dest);
269 ftp_use(thisftp);
270 return 0;
271 }
272 else if(a == ASKNO) {
273 free(dest);
274 ftp_use(thisftp);
275 return 0;
276 }
277 else if(a == ASKUNIQUE)
278 opt |= FXP_UNIQUE; /* for this file only */
279 else if(a == ASKALL)
280 fxp_owbatch = true;
281 else if(a == ASKRESUME)
282 opt |= FXP_RESUME; /* for this file only */
283 /* else a == ASKYES */
284 }
285 }
286 }
287
288 if(test(opt, FXP_RESUME))
289 how = fxpResume;
290 if(test(opt, FXP_UNIQUE))
291 how = fxpUnique;
292
293 r = do_the_fxp(thisftp, fi->path, fxp_target, dest, how, opt);
294 free(dest);
295 if(r != 0) {
296 ftp_use(thisftp);
297 return -1;
298 }
299
300 if(test(opt, FXP_PRESERVE))
301 fxp_preserve_attribs(fi, dest);
302
303 if(test(opt, FXP_DELETE_AFTER)) {
304 bool dodel = false;
305
306 ftp_use(thisftp);
307
308 if(!test(opt, FXP_FORCE)
309 && !fxp_delbatch && !gvSighupReceived)
310 {
311 char* sp = shortpath(fi->path, 42, ftp->homedir);
312 int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
313 _("Delete remote file '%s'?"),
314 sp);
315 free(sp);
316 if(a == ASKALL) {
317 fxp_delbatch = true;
318 dodel = true;
319 }
320 else if(a == ASKCANCEL)
321 fxp_quit = true;
322 else if(a != ASKNO)
323 dodel = true;
324 } else
325 dodel = true;
326
327 if(dodel) {
328 ftp_unlink(fi->path);
329 char* sp = shortpath(fi->path, 42, ftp->homedir);
330 if(ftp->code == ctComplete)
331 fprintf(stderr, _("%s: deleted\n"), sp);
332 else
333 fprintf(stderr, _("error deleting '%s': %s\n"), sp,
334 ftp_getreply(false));
335 free(sp);
336 }
337 }
338
339 ftp_use(thisftp);
340 return 0;
341 }
342
fxpfiles(list * gl,unsigned int opt,const char * output)343 static void fxpfiles(list *gl, unsigned int opt, const char *output)
344 {
345 listitem *li;
346 rfile *fp, *lnfp;
347 const char *opath, *ofile;
348 char *link = 0;
349
350 list_sort(gl, get_sort_func, false);
351
352 li = gl->first;
353 while(li && !fxp_quit) {
354 fp = (rfile *)li->data;
355
356 if(!ftp_connected())
357 return;
358
359 if(gvSighupReceived) {
360 if(!test(opt, FXP_RESUME))
361 opt |= FXP_UNIQUE;
362 opt |= FXP_FORCE;
363 }
364
365 opath = fp->path;
366 ofile = base_name_ptr(opath);
367
368 if(strcmp(ofile, ".")==0 || strcmp(ofile, "..")==0) {
369 transfer_nextfile(gl, &li, true);
370 continue;
371 }
372
373 if(test(opt, FXP_INTERACTIVE) && !fxp_batch && !gvSighupReceived) {
374 char* sp = shortpath(opath, 42, ftp->homedir);
375 int a = ask(ASKYES|ASKNO|ASKCANCEL|ASKALL, ASKYES,
376 _("Get '%s'?"),
377 sp);
378 free(sp);
379 if(a == ASKNO) {
380 transfer_nextfile(gl, &li, true);
381 continue;
382 }
383 if(a == ASKCANCEL) {
384 fxp_quit = true;
385 break;
386 }
387 if(a == ASKALL)
388 fxp_batch = true;
389 /* else a==ASKYES */
390 }
391
392 if(rislink(fp)) {
393 link_to_link__duh:
394 {
395 char *xcurdir = base_dir_xptr(opath);
396 link = path_absolute(fp->link, xcurdir, ftp->homedir);
397 stripslash(link);
398 free(xcurdir);
399 ftp_trace("found link: '%s' -> '%s'\n", opath, link);
400 }
401
402 lnfp = ftp_get_file(link);
403 if(lnfp == 0) {
404 /* couldn't dereference the link, try to RETR it */
405 ftp_trace("unable to dereference link\n");
406 int r = fxpfile(fp, opt, output, ofile);
407 transfer_nextfile(gl, &li, r == 0);
408 continue;
409 }
410
411 if(strncmp(opath, lnfp->path, strlen(lnfp->path)) == 0) {
412 ftp_trace("opath == '%s', lnfp->path == '%s'\n", opath,
413 lnfp->path);
414 char* sp = shortpath(lnfp->path, 42, ftp->homedir);
415 fprintf(stderr, _("%s: circular link -- skipping\n"), sp);
416 free(sp);
417 transfer_nextfile(gl, &li, true);
418 continue;
419 }
420
421 fp = lnfp;
422
423 if(rislink(fp))
424 /* found a link pointing to another link
425 */
426 goto link_to_link__duh;
427 }
428
429 if(risdir(fp)) {
430 if(test(opt, FXP_RECURSIVE)) {
431 if((fxp_dir_glob_mask
432 && fnmatch(fxp_dir_glob_mask,
433 base_name_ptr(fp->path),
434 FNM_EXTMATCH) == FNM_NOMATCH)
435 #ifdef HAVE_REGEX
436 || (fxp_dir_rx_mask_set
437 && regexec(&fxp_dir_rx_mask, base_name_ptr(fp->path),
438 0, 0, 0) == REG_NOMATCH)
439 #endif
440 )
441 {
442 /*printf("skipping %s\n", fp->path);*/
443 } else {
444 char* recurs_output = NULL;
445 bool success = true;
446 if(!test(opt, FXP_PARENTS))
447 success = asprintf(&recurs_output, "%s/%s",
448 output ? output : ".", ofile) != -1;
449 else
450 success = asprintf(&recurs_output, "%s",
451 output ? output : ".") != -1;
452 if (!success)
453 {
454 fprintf(stderr, _("Failed to allocate memory.\n"));
455 transfer_nextfile(gl, &li, true);
456 continue;
457 }
458
459 char* recurs_mask = NULL;
460 if (asprintf(&recurs_mask, "%s/*", opath) == -1)
461 {
462 free(recurs_output);
463 fprintf(stderr, _("Failed to allocate memory.\n"));
464 transfer_nextfile(gl, &li, true);
465 continue;
466 }
467
468 char* q_recurs_mask = backslash_quote(recurs_mask);
469 list* rgl = rglob_create();
470 rglob_glob(rgl, q_recurs_mask, true, true,
471 fxp_exclude_func);
472 free(q_recurs_mask);
473 if(list_numitem(rgl) > 0)
474 fxpfiles(rgl, opt, recurs_output);
475 if(test(opt, FXP_PRESERVE))
476 fxp_preserve_attribs(fp, recurs_output);
477 rglob_destroy(rgl);
478 free(recurs_output);
479 }
480 } else if(test(opt, FXP_VERBOSE)) {
481 char* sp = shortpath(opath, 42, ftp->homedir);
482 fprintf(stderr, _("%s: omitting directory\n"), sp);
483 free(sp);
484 }
485 transfer_nextfile(gl, &li, true);
486 continue;
487 }
488 if(!risreg(fp)) {
489 if(test(opt, FXP_VERBOSE)) {
490 char* sp = shortpath(opath, 42, ftp->homedir);
491 fprintf(stderr, _("%s: not a regular file\n"), sp);
492 free(sp);
493 }
494 transfer_nextfile(gl, &li, true);
495 continue;
496 }
497 const int r = fxpfile(fp, opt, output, ofile);
498
499 transfer_nextfile(gl, &li, r == 0);
500
501 if(gvInterrupted) {
502 gvInterrupted = false;
503 if(li && !fxp_quit && ftp_connected() && !gvSighupReceived)
504 {
505 int a = ask(ASKYES|ASKNO, ASKYES,
506 _("Continue transfer?"));
507 if(a == ASKNO) {
508 fxp_quit = true;
509 break;
510 }
511 /* else a == ASKYES */
512 fprintf(stderr, _("Excellent!!!\n"));
513 }
514 }
515 }
516 }
517
cmd_fxp(int argc,char ** argv)518 void cmd_fxp(int argc, char **argv)
519 {
520 list *gl;
521 listitem *fxp_tmp = 0;
522 char *logfile = 0;
523 char *fxp_output = 0;
524 #ifdef HAVE_REGEX
525 int ret;
526 char fxp_rx_errbuf[129];
527 #endif
528 int c, opt = FXP_VERBOSE;
529 struct option longopts[] = {
530 {"append", no_argument, 0, 'a'},
531 {"delete-after", no_argument, 0, 'D'},
532 {"dir-mask", required_argument, 0, '3'},
533 #ifdef HAVE_REGEX
534 {"dir-rx-mask", required_argument, 0, '4'},
535 #endif
536 {"force", no_argument, 0, 'f'},
537 {"force-newer", no_argument, 0, 'F'},
538 {"nohup", no_argument, 0, 'H'},
539 {"interactive", no_argument, 0, 'i'},
540 {"logfile", required_argument, 0, 'L'},
541 {"mask", required_argument, 0, 'm'},
542 #ifdef HAVE_REGEX
543 {"rx-mask", required_argument, 0, 'M'},
544 #endif
545 {"newer", no_argument, 0, 'n'},
546 {"output", required_argument, 0, 'o'},
547 {"preserve", no_argument, 0, 'p'},
548 {"parents", no_argument, 0, 'P'},
549 {"quiet", no_argument, 0, 'q'},
550 {"recursive", no_argument, 0, 'r'},
551 {"resume", no_argument, 0, 'R'},
552 {"skip-existing", no_argument, 0, 's'},
553 {"tagged", no_argument, 0, 't'},
554 {"target", required_argument, 0, 'T'},
555 {"type", required_argument, 0, '1'},
556 {"unique", no_argument, 0, 'u'},
557 {"verbose", no_argument, 0, 'v'},
558 {"help", no_argument, 0, 'h'},
559 {0, 0, 0, 0}
560 };
561
562 if(fxp_glob_mask) {
563 free(fxp_glob_mask);
564 fxp_glob_mask = 0;
565 }
566 if(fxp_dir_glob_mask) {
567 free(fxp_dir_glob_mask);
568 fxp_dir_glob_mask = 0;
569 }
570 #ifdef HAVE_REGEX
571 if(fxp_rx_mask_set) {
572 fxp_rx_mask_set = 0;
573 }
574 if(fxp_dir_rx_mask_set) {
575 regfree(&fxp_dir_rx_mask);
576 fxp_dir_rx_mask_set = 0;
577 }
578 #endif
579
580 if(list_numitem(gvFtpList) == 2) {
581 fxp_tmp = gvFtpList->first;
582 if(fxp_tmp->data == ftp)
583 fxp_target = fxp_tmp->next->data;
584 else
585 fxp_target = fxp_tmp->data;
586 } else
587 fxp_target = 0;
588
589 fxp_skip_empty = false;
590
591 optind = 0; /* force getopt() to re-initialize */
592 while((c=getopt_long(argc, argv, "aDefFHiL:M:no:pPqrRstT:uvh",
593 longopts, 0)) != EOF)
594 {
595 switch(c) {
596 case 'a': /* --append */
597 opt |= FXP_APPEND;
598 break;
599 case 'D': /* --delete-after */
600 opt |= FXP_DELETE_AFTER;
601 break;
602 case 'f': /* --force */
603 opt |= FXP_FORCE;
604 break;
605 case 'F':
606 opt |= FXP_FORCE_NEWER;
607 break;
608 case 'e': /* --skip-empty */
609 opt |= FXP_SKIP_EMPTY;
610 fxp_skip_empty = true;
611 break;
612 case '3': /* --dir-mask=GLOB */
613 free(fxp_dir_glob_mask);
614 fxp_dir_glob_mask = xstrdup(optarg);
615 unquote(fxp_dir_glob_mask);
616 break;
617 #ifdef HAVE_REGEX
618 case '4': /* --dir-rx-mask=REGEXP */
619 if(fxp_dir_rx_mask_set) {
620 regfree(&fxp_dir_rx_mask);
621 fxp_dir_rx_mask_set = false;
622 }
623 unquote(optarg);
624 ret = regcomp(&fxp_dir_rx_mask, optarg, REG_EXTENDED);
625 if(ret != 0) {
626 regerror(ret, &fxp_dir_rx_mask, fxp_rx_errbuf, 128);
627 ftp_err(_("Regexp '%s' failed: %s\n"),
628 optarg, fxp_rx_errbuf);
629 return;
630 } else
631 fxp_dir_rx_mask_set = true;
632 break;
633 #endif
634 case 'H': /* --nohup */
635 opt |= FXP_NOHUP;
636 break;
637 case 'i': /* --interactive */
638 opt |= FXP_INTERACTIVE;
639 break;
640 case 'L': /* --logfile=FILE */
641 free(logfile);
642 logfile = xstrdup(optarg);
643 unquote(logfile);
644 break;
645 case 'm': /* --mask=GLOB */
646 free(fxp_glob_mask);
647 fxp_glob_mask = xstrdup(optarg);
648 unquote(fxp_glob_mask);
649 break;
650 #ifdef HAVE_REGEX
651 case 'M': /* --rx-mask=REGEXP */
652 if(fxp_rx_mask_set) {
653 regfree(&fxp_rx_mask);
654 fxp_rx_mask_set = false;
655 }
656
657 unquote(optarg);
658 ret = regcomp(&fxp_rx_mask, optarg, REG_EXTENDED);
659 if(ret != 0) {
660 regerror(ret, &fxp_rx_mask, fxp_rx_errbuf, 128);
661 ftp_err(_("Regexp '%s' failed: %s\n"),
662 optarg, fxp_rx_errbuf);
663 return;
664 } else
665 fxp_rx_mask_set = true;
666 break;
667 #endif
668 case 'n': /* --newer */
669 opt |= FXP_NEWER;
670 break;
671 case 'o': /* --output=DIRECTORY */
672 if(fxp_target == 0) {
673 printf(_("FxP target not set, use --target=NAME"
674 " (as first option)\n"));
675 return;
676 }
677 fxp_output = tilde_expand_home(optarg, fxp_target->homedir);
678 stripslash(fxp_output);
679 unquote(fxp_output);
680 break;
681 case 'p': /* --preserve */
682 opt |= FXP_PRESERVE;
683 break;
684 case 'P': /* --parents */
685 opt |= FXP_PARENTS;
686 break;
687 case 'q': /* --quiet */
688 opt &= ~FXP_VERBOSE;
689 break;
690 case 'r': /* --recursive */
691 opt |= FXP_RECURSIVE;
692 break;
693 case 'R': /* --resume */
694 opt |= FXP_RESUME;
695 break;
696 case 's':
697 opt |= FXP_SKIP_EXISTING;
698 break;
699 case 't': /* --tagged */
700 opt |= FXP_TAGGED;
701 break;
702 case '1': /* --type=[ascii|binary] */
703 if(strncmp(optarg, "ascii", strlen(optarg)) == 0)
704 opt |= FXP_ASCII;
705 else if(strncmp(optarg, "binary", strlen(optarg)) == 0)
706 opt |= FXP_BINARY;
707 else {
708 printf(_("Invalid option argument --type=%s\n"), optarg);
709 return;
710 }
711 break;
712 case 'T': /* --target=HOST */
713 fxp_tmp = ftplist_search(optarg);
714 if(!fxp_tmp)
715 return;
716 fxp_target = (Ftp *)fxp_tmp->data;
717 break;
718 case 'u': /* --unique */
719 opt |= FXP_UNIQUE;
720 break;
721 case 'v': /* --verbose */
722 opt |= FXP_VERBOSE;
723 break;
724 case 'h': /* --help */
725 print_fxp_syntax();
726 return;
727 case '?':
728 default:
729 return;
730 }
731 }
732
733 if(optind >= argc && !test(opt, FXP_TAGGED)) {
734 minargs(optind);
735 return;
736 }
737
738 need_connected();
739 need_loggedin();
740
741 if(fxp_target == 0) {
742 ftp_err(_("No target specified, try '%s --help'"
743 " for more information\n"), argv[0]);
744 return;
745 }
746
747 #ifdef HAVE_LIBSSH
748 if(ftp->session || fxp_target->session) {
749 ftp_err("FxP for SSH connections no implemented\n");
750 return;
751 }
752 #endif
753
754 gl = rglob_create();
755 while(optind < argc) {
756 stripslash(argv[optind]);
757 if(rglob_glob(gl, argv[optind], true, true, fxp_exclude_func) == -1)
758 fprintf(stderr, _("%s: no matches found\n"), argv[optind]);
759 optind++;
760 }
761 if(list_numitem(gl) == 0 && !test(opt, FXP_TAGGED)) {
762 rglob_destroy(gl);
763 return;
764 }
765 if(test(opt, FXP_TAGGED)
766 && (!ftp->taglist || list_numitem(ftp->taglist) == 0))
767 {
768 printf(_("no tagged files\n"));
769 if(list_numitem(gl) == 0) {
770 rglob_destroy(gl);
771 return;
772 }
773 }
774
775 fxp_quit = false;
776 fxp_batch = fxp_owbatch = fxp_delbatch = test(opt, FXP_FORCE);
777 if(test(opt, FXP_FORCE))
778 opt &= ~FXP_INTERACTIVE;
779
780 if(fxp_output && !test(opt, FXP_RECURSIVE) && list_numitem(gl) +
781 (test(opt, FXP_TAGGED) ? list_numitem(ftp->taglist) : 0) == 1)
782 {
783 opt |= FXP_OUTPUT_FILE;
784 }
785
786 gvInTransfer = true;
787 gvInterrupted = false;
788
789 if(test(opt, FXP_NOHUP)) {
790 int r = 0;
791 pid_t pid = fork();
792
793 if(pid == 0) {
794 r = transfer_init_nohup(logfile);
795 if(r != 0)
796 exit(0);
797 }
798
799 if(r != 0)
800 return;
801
802 if(pid == 0) { /* child process */
803 transfer_begin_nohup(argc, argv);
804
805 if(!test(opt, FXP_FORCE) && !test(opt, FXP_RESUME))
806 opt |= FXP_UNIQUE;
807 opt |= FXP_FORCE;
808
809 if(list_numitem(gl))
810 fxpfiles(gl, opt, fxp_output);
811 rglob_destroy(gl);
812
813 if(ftp->taglist && test(opt, FXP_TAGGED))
814 fxpfiles(ftp->taglist, opt, fxp_output);
815
816 free(fxp_output);
817
818 transfer_end_nohup();
819 }
820 if(pid == -1) {
821 perror("fork()");
822 return;
823 }
824 /* parent process */
825 sleep(1);
826 printf("%d\n", pid);
827 input_save_history();
828 gvars_destroy();
829 reset_xterm_title();
830 exit(0);
831 }
832
833 if(list_numitem(gl))
834 fxpfiles(gl, opt, fxp_output);
835 rglob_destroy(gl);
836
837 if(ftp->taglist && test(opt, FXP_TAGGED))
838 fxpfiles(ftp->taglist, opt, fxp_output);
839
840 free(fxp_output);
841 gvInTransfer = false;
842 }
843