cmds.c revision 1.64 1 /* $NetBSD: cmds.c,v 1.64 1999/09/30 12:18:03 lukem Exp $ */
2
3 /*
4 * Copyright (C) 1997 and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
38 * NASA Ames Research Center.
39 *
40 * This code is derived from software contributed to The NetBSD Foundation
41 * by Luke Mewburn.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the NetBSD
54 * Foundation, Inc. and its contributors.
55 * 4. Neither the name of The NetBSD Foundation nor the names of its
56 * contributors may be used to endorse or promote products derived
57 * from this software without specific prior written permission.
58 *
59 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
60 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
61 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
62 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
63 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
64 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
65 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
66 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
67 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
68 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
69 * POSSIBILITY OF SUCH DAMAGE.
70 */
71
72 /*
73 * Copyright (c) 1985, 1989, 1993, 1994
74 * The Regents of the University of California. All rights reserved.
75 *
76 * Redistribution and use in source and binary forms, with or without
77 * modification, are permitted provided that the following conditions
78 * are met:
79 * 1. Redistributions of source code must retain the above copyright
80 * notice, this list of conditions and the following disclaimer.
81 * 2. Redistributions in binary form must reproduce the above copyright
82 * notice, this list of conditions and the following disclaimer in the
83 * documentation and/or other materials provided with the distribution.
84 * 3. All advertising materials mentioning features or use of this software
85 * must display the following acknowledgement:
86 * This product includes software developed by the University of
87 * California, Berkeley and its contributors.
88 * 4. Neither the name of the University nor the names of its contributors
89 * may be used to endorse or promote products derived from this software
90 * without specific prior written permission.
91 *
92 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
93 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
95 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
96 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
97 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
98 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
99 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
100 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
101 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
102 * SUCH DAMAGE.
103 */
104
105 #include <sys/cdefs.h>
106 #ifndef lint
107 #if 0
108 static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94";
109 #else
110 __RCSID("$NetBSD: cmds.c,v 1.64 1999/09/30 12:18:03 lukem Exp $");
111 #endif
112 #endif /* not lint */
113
114 /*
115 * FTP User Program -- Command Routines.
116 */
117 #include <sys/types.h>
118 #include <sys/socket.h>
119 #include <sys/stat.h>
120 #include <sys/wait.h>
121 #include <arpa/ftp.h>
122
123 #include <ctype.h>
124 #include <err.h>
125 #include <glob.h>
126 #include <limits.h>
127 #include <netdb.h>
128 #include <stdio.h>
129 #include <stdlib.h>
130 #include <string.h>
131 #include <time.h>
132 #include <unistd.h>
133
134 #include "ftp_var.h"
135 #include "pathnames.h"
136
137 jmp_buf jabort;
138 char *mname;
139
140 struct types {
141 char *t_name;
142 char *t_mode;
143 int t_type;
144 char *t_arg;
145 } types[] = {
146 { "ascii", "A", TYPE_A, 0 },
147 { "binary", "I", TYPE_I, 0 },
148 { "image", "I", TYPE_I, 0 },
149 { "ebcdic", "E", TYPE_E, 0 },
150 { "tenex", "L", TYPE_L, bytename },
151 { NULL }
152 };
153
154 /*
155 * Set transfer type.
156 */
157 void
158 settype(argc, argv)
159 int argc;
160 char *argv[];
161 {
162 struct types *p;
163 int comret;
164
165 if (argc > 2) {
166 char *sep;
167
168 fprintf(ttyout, "usage: %s [", argv[0]);
169 sep = " ";
170 for (p = types; p->t_name; p++) {
171 fprintf(ttyout, "%s%s", sep, p->t_name);
172 sep = " | ";
173 }
174 fputs(" ]\n", ttyout);
175 code = -1;
176 return;
177 }
178 if (argc < 2) {
179 fprintf(ttyout, "Using %s mode to transfer files.\n", typename);
180 code = 0;
181 return;
182 }
183 for (p = types; p->t_name; p++)
184 if (strcmp(argv[1], p->t_name) == 0)
185 break;
186 if (p->t_name == 0) {
187 fprintf(ttyout, "%s: unknown mode.\n", argv[1]);
188 code = -1;
189 return;
190 }
191 if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
192 comret = command("TYPE %s %s", p->t_mode, p->t_arg);
193 else
194 comret = command("TYPE %s", p->t_mode);
195 if (comret == COMPLETE) {
196 (void)strlcpy(typename, p->t_name, sizeof(typename));
197 curtype = type = p->t_type;
198 }
199 }
200
201 /*
202 * Internal form of settype; changes current type in use with server
203 * without changing our notion of the type for data transfers.
204 * Used to change to and from ascii for listings.
205 */
206 void
207 changetype(newtype, show)
208 int newtype, show;
209 {
210 struct types *p;
211 int comret, oldverbose = verbose;
212
213 if (newtype == 0)
214 newtype = TYPE_I;
215 if (newtype == curtype)
216 return;
217 if (debug == 0 && show == 0)
218 verbose = 0;
219 for (p = types; p->t_name; p++)
220 if (newtype == p->t_type)
221 break;
222 if (p->t_name == 0) {
223 warnx("internal error: unknown type %d.", newtype);
224 return;
225 }
226 if (newtype == TYPE_L && bytename[0] != '\0')
227 comret = command("TYPE %s %s", p->t_mode, bytename);
228 else
229 comret = command("TYPE %s", p->t_mode);
230 if (comret == COMPLETE)
231 curtype = newtype;
232 verbose = oldverbose;
233 }
234
235 char *stype[] = {
236 "type",
237 "",
238 0
239 };
240
241 /*
242 * Set binary transfer type.
243 */
244 /*VARARGS*/
245 void
246 setbinary(argc, argv)
247 int argc;
248 char *argv[];
249 {
250
251 stype[1] = "binary";
252 settype(2, stype);
253 }
254
255 /*
256 * Set ascii transfer type.
257 */
258 /*VARARGS*/
259 void
260 setascii(argc, argv)
261 int argc;
262 char *argv[];
263 {
264
265 stype[1] = "ascii";
266 settype(2, stype);
267 }
268
269 /*
270 * Set tenex transfer type.
271 */
272 /*VARARGS*/
273 void
274 settenex(argc, argv)
275 int argc;
276 char *argv[];
277 {
278
279 stype[1] = "tenex";
280 settype(2, stype);
281 }
282
283 /*
284 * Set file transfer mode.
285 */
286 /*ARGSUSED*/
287 void
288 setftmode(argc, argv)
289 int argc;
290 char *argv[];
291 {
292
293 fprintf(ttyout, "We only support %s mode, sorry.\n", modename);
294 code = -1;
295 }
296
297 /*
298 * Set file transfer format.
299 */
300 /*ARGSUSED*/
301 void
302 setform(argc, argv)
303 int argc;
304 char *argv[];
305 {
306
307 fprintf(ttyout, "We only support %s format, sorry.\n", formname);
308 code = -1;
309 }
310
311 /*
312 * Set file transfer structure.
313 */
314 /*ARGSUSED*/
315 void
316 setstruct(argc, argv)
317 int argc;
318 char *argv[];
319 {
320
321 fprintf(ttyout, "We only support %s structure, sorry.\n", structname);
322 code = -1;
323 }
324
325 /*
326 * Send a single file.
327 */
328 void
329 put(argc, argv)
330 int argc;
331 char *argv[];
332 {
333 char *cmd;
334 int loc = 0;
335 char *locfile, *remfile;
336
337 if (argc == 2) {
338 argc++;
339 argv[2] = argv[1];
340 loc++;
341 }
342 if (argc < 2 && !another(&argc, &argv, "local-file"))
343 goto usage;
344 if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) {
345 usage:
346 fprintf(ttyout, "usage: %s local-file [ remote-file ]\n",
347 argv[0]);
348 code = -1;
349 return;
350 }
351 if ((locfile = globulize(argv[1])) == NULL) {
352 code = -1;
353 return;
354 }
355 remfile = argv[2];
356 if (loc) /* If argv[2] is a copy of the old argv[1], update it */
357 remfile = locfile;
358 cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
359 if (loc && ntflag)
360 remfile = dotrans(remfile);
361 if (loc && mapflag)
362 remfile = domap(remfile);
363 sendrequest(cmd, locfile, remfile,
364 locfile != argv[1] || remfile != argv[2]);
365 free(locfile);
366 }
367
368 /*
369 * Send multiple files.
370 */
371 void
372 mput(argc, argv)
373 int argc;
374 char *argv[];
375 {
376 int i;
377 sig_t oldintr;
378 int ointer;
379 char *tp;
380
381 if (argc < 2 && !another(&argc, &argv, "local-files")) {
382 fprintf(ttyout, "usage: %s local-files\n", argv[0]);
383 code = -1;
384 return;
385 }
386 mname = argv[0];
387 mflag = 1;
388 oldintr = xsignal(SIGINT, mabort);
389 (void)setjmp(jabort);
390 if (proxy) {
391 char *cp;
392
393 while ((cp = remglob(argv, 0, NULL)) != NULL) {
394 if (*cp == '\0') {
395 mflag = 0;
396 continue;
397 }
398 if (mflag && confirm(argv[0], cp)) {
399 tp = cp;
400 if (mcase)
401 tp = docase(tp);
402 if (ntflag)
403 tp = dotrans(tp);
404 if (mapflag)
405 tp = domap(tp);
406 sendrequest((sunique) ? "STOU" : "STOR",
407 cp, tp, cp != tp || !interactive);
408 if (!mflag && fromatty) {
409 ointer = interactive;
410 interactive = 1;
411 if (confirm("Continue with", "mput")) {
412 mflag++;
413 }
414 interactive = ointer;
415 }
416 }
417 }
418 (void)xsignal(SIGINT, oldintr);
419 mflag = 0;
420 return;
421 }
422 for (i = 1; i < argc; i++) {
423 char **cpp;
424 glob_t gl;
425 int flags;
426
427 if (!doglob) {
428 if (mflag && confirm(argv[0], argv[i])) {
429 tp = (ntflag) ? dotrans(argv[i]) : argv[i];
430 tp = (mapflag) ? domap(tp) : tp;
431 sendrequest((sunique) ? "STOU" : "STOR",
432 argv[i], tp, tp != argv[i] || !interactive);
433 if (!mflag && fromatty) {
434 ointer = interactive;
435 interactive = 1;
436 if (confirm("Continue with", "mput")) {
437 mflag++;
438 }
439 interactive = ointer;
440 }
441 }
442 continue;
443 }
444
445 memset(&gl, 0, sizeof(gl));
446 flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
447 if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
448 warnx("%s: not found", argv[i]);
449 globfree(&gl);
450 continue;
451 }
452 for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
453 if (mflag && confirm(argv[0], *cpp)) {
454 tp = (ntflag) ? dotrans(*cpp) : *cpp;
455 tp = (mapflag) ? domap(tp) : tp;
456 sendrequest((sunique) ? "STOU" : "STOR",
457 *cpp, tp, *cpp != tp || !interactive);
458 if (!mflag && fromatty) {
459 ointer = interactive;
460 interactive = 1;
461 if (confirm("Continue with", "mput")) {
462 mflag++;
463 }
464 interactive = ointer;
465 }
466 }
467 }
468 globfree(&gl);
469 }
470 (void)xsignal(SIGINT, oldintr);
471 mflag = 0;
472 }
473
474 void
475 reget(argc, argv)
476 int argc;
477 char *argv[];
478 {
479
480 (void)getit(argc, argv, 1, "r+w");
481 }
482
483 void
484 get(argc, argv)
485 int argc;
486 char *argv[];
487 {
488
489 (void)getit(argc, argv, 0, restart_point ? "r+w" : "w" );
490 }
491
492 /*
493 * Receive one file.
494 */
495 int
496 getit(argc, argv, restartit, mode)
497 int argc;
498 char *argv[];
499 int restartit;
500 const char *mode;
501 {
502 int loc = 0;
503 int rval = 0;
504 char *remfile, *locfile, *olocfile;
505
506 if (argc == 2) {
507 argc++;
508 argv[2] = argv[1];
509 loc++;
510 }
511 if (argc < 2 && !another(&argc, &argv, "remote-file"))
512 goto usage;
513 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) {
514 usage:
515 fprintf(ttyout, "usage: %s remote-file [ local-file ]\n",
516 argv[0]);
517 code = -1;
518 return (0);
519 }
520 remfile = argv[1];
521 if ((olocfile = globulize(argv[2])) == NULL) {
522 code = -1;
523 return (0);
524 }
525 locfile = olocfile;
526 if (loc && mcase)
527 locfile = docase(locfile);
528 if (loc && ntflag)
529 locfile = dotrans(locfile);
530 if (loc && mapflag)
531 locfile = domap(locfile);
532 if (restartit) {
533 struct stat stbuf;
534 int ret;
535
536 ret = stat(locfile, &stbuf);
537 if (restartit == 1) {
538 if (ret < 0) {
539 warn("local: %s", locfile);
540 goto freegetit;
541 }
542 restart_point = stbuf.st_size;
543 } else {
544 if (ret == 0) {
545 time_t mtime;
546
547 mtime = remotemodtime(argv[1], 0);
548 if (mtime == -1)
549 goto freegetit;
550 if (stbuf.st_mtime >= mtime) {
551 rval = 1;
552 goto freegetit;
553 }
554 }
555 }
556 }
557
558 recvrequest("RETR", locfile, remfile, mode,
559 remfile != argv[1] || locfile != argv[2], loc);
560 restart_point = 0;
561 freegetit:
562 (void)free(olocfile);
563 return (rval);
564 }
565
566 /* ARGSUSED */
567 void
568 mabort(signo)
569 int signo;
570 {
571 int ointer, oconf;
572
573 alarmtimer(0);
574 putc('\n', ttyout);
575 if (mflag && fromatty) {
576 ointer = interactive;
577 oconf = confirmrest;
578 interactive = 1;
579 confirmrest = 0;
580 if (confirm("Continue with", mname)) {
581 interactive = ointer;
582 confirmrest = oconf;
583 longjmp(jabort, 0);
584 }
585 interactive = ointer;
586 confirmrest = oconf;
587 }
588 mflag = 0;
589 longjmp(jabort, 0);
590 }
591
592 /*
593 * Get multiple files.
594 */
595 void
596 mget(argc, argv)
597 int argc;
598 char *argv[];
599 {
600 sig_t oldintr;
601 int ch, ointer;
602 char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
603
604 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
605 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
606 code = -1;
607 return;
608 }
609 mname = argv[0];
610 mflag = 1;
611 oldintr = xsignal(SIGINT, mabort);
612 (void)setjmp(jabort);
613 while ((cp = remglob(argv, proxy, NULL)) != NULL) {
614 if (*cp == '\0') {
615 mflag = 0;
616 continue;
617 }
618 if (mflag && confirm(argv[0], cp)) {
619 tp = cp;
620 if (mcase) {
621 for (tp2 = tmpbuf; (ch = *tp++) != 0; )
622 *tp2++ = isupper(ch) ? tolower(ch) : ch;
623 *tp2 = '\0';
624 tp = tmpbuf;
625 }
626 if (ntflag) {
627 tp = dotrans(tp);
628 }
629 if (mapflag) {
630 tp = domap(tp);
631 }
632 recvrequest("RETR", tp, cp, "w",
633 tp != cp || !interactive, 1);
634 if (!mflag && fromatty) {
635 ointer = interactive;
636 interactive = 1;
637 if (confirm("Continue with", "mget")) {
638 mflag++;
639 }
640 interactive = ointer;
641 }
642 }
643 }
644 (void)xsignal(SIGINT, oldintr);
645 mflag = 0;
646 }
647
648 char *
649 onoff(bool)
650 int bool;
651 {
652
653 return (bool ? "on" : "off");
654 }
655
656 /*
657 * Show status.
658 */
659 /*ARGSUSED*/
660 void
661 status(argc, argv)
662 int argc;
663 char *argv[];
664 {
665 int i;
666
667 if (connected)
668 fprintf(ttyout, "Connected %sto %s.\n",
669 connected == -1 ? "and logged in" : "", hostname);
670 else
671 fputs("Not connected.\n", ttyout);
672 if (!proxy) {
673 pswitch(1);
674 if (connected) {
675 fprintf(ttyout, "Connected for proxy commands to %s.\n",
676 hostname);
677 }
678 else {
679 fputs("No proxy connection.\n", ttyout);
680 }
681 pswitch(0);
682 }
683 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode),
684 *gateserver ? gateserver : "(none)", gateport);
685 fprintf(ttyout, "Passive mode: %s.\n", onoff(passivemode));
686 fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n",
687 modename, typename, formname, structname);
688 fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n",
689 onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob));
690 fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n",
691 onoff(sunique), onoff(runique));
692 fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve));
693 fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase),
694 onoff(crflag));
695 if (ntflag) {
696 fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout);
697 }
698 else {
699 fputs("Ntrans: off.\n", ttyout);
700 }
701 if (mapflag) {
702 fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout);
703 }
704 else {
705 fputs("Nmap: off.\n", ttyout);
706 }
707 fprintf(ttyout,
708 "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n",
709 onoff(hash), mark, onoff(progress));
710 fprintf(ttyout,
711 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
712 onoff(rate_get), rate_get, rate_get_incr);
713 fprintf(ttyout,
714 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
715 onoff(rate_put), rate_put, rate_put_incr);
716 fprintf(ttyout,
717 "Socket buffer sizes: send %d, receive %d.\n",
718 sndbuf_size, rcvbuf_size);
719 fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport));
720 fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s.\n", onoff(epsv4));
721 #ifndef NO_EDITCOMPLETE
722 fprintf(ttyout, "Command line editing: %s.\n", onoff(editing));
723 #endif /* !NO_EDITCOMPLETE */
724 if (macnum > 0) {
725 fputs("Macros:\n", ttyout);
726 for (i=0; i<macnum; i++) {
727 fprintf(ttyout, "\t%s\n", macros[i].mac_name);
728 }
729 }
730 code = 0;
731 }
732
733 /*
734 * Toggle a variable
735 */
736 int
737 togglevar(argc, argv, var, mesg)
738 int argc;
739 char *argv[];
740 int *var;
741 const char *mesg;
742 {
743 if (argc < 2) {
744 *var = !*var;
745 } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) {
746 *var = 1;
747 } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) {
748 *var = 0;
749 } else {
750 fprintf(ttyout, "usage: %s [ on | off ]\n", argv[0]);
751 return (-1);
752 }
753 if (mesg)
754 fprintf(ttyout, "%s %s.\n", mesg, onoff(*var));
755 return (*var);
756 }
757
758 /*
759 * Set beep on cmd completed mode.
760 */
761 /*VARARGS*/
762 void
763 setbell(argc, argv)
764 int argc;
765 char *argv[];
766 {
767
768 code = togglevar(argc, argv, &bell, "Bell mode");
769 }
770
771 #ifndef NO_EDITCOMPLETE
772 /*
773 * Set command line editing
774 */
775 /*VARARGS*/
776 void
777 setedit(argc, argv)
778 int argc;
779 char *argv[];
780 {
781
782 code = togglevar(argc, argv, &editing, "Editing mode");
783 controlediting();
784 }
785 #endif /* !NO_EDITCOMPLETE */
786
787 /*
788 * Turn on packet tracing.
789 */
790 /*VARARGS*/
791 void
792 settrace(argc, argv)
793 int argc;
794 char *argv[];
795 {
796
797 code = togglevar(argc, argv, &trace, "Packet tracing");
798 }
799
800 /*
801 * Toggle hash mark printing during transfers, or set hash mark bytecount.
802 */
803 /*VARARGS*/
804 void
805 sethash(argc, argv)
806 int argc;
807 char *argv[];
808 {
809 if (argc == 1)
810 hash = !hash;
811 else if (argc != 2) {
812 fprintf(ttyout, "usage: %s [ on | off | bytecount ]\n",
813 argv[0]);
814 code = -1;
815 return;
816 } else if (strcasecmp(argv[1], "on") == 0)
817 hash = 1;
818 else if (strcasecmp(argv[1], "off") == 0)
819 hash = 0;
820 else {
821 int nmark;
822
823 nmark = strsuftoi(argv[1]);
824 if (nmark < 1) {
825 fprintf(ttyout, "mark: bad bytecount value `%s'.\n",
826 argv[1]);
827 code = -1;
828 return;
829 }
830 mark = nmark;
831 hash = 1;
832 }
833 fprintf(ttyout, "Hash mark printing %s", onoff(hash));
834 if (hash)
835 fprintf(ttyout, " (%d bytes/hash mark)", mark);
836 fputs(".\n", ttyout);
837 if (hash)
838 progress = 0;
839 code = hash;
840 }
841
842 /*
843 * Turn on printing of server echo's.
844 */
845 /*VARARGS*/
846 void
847 setverbose(argc, argv)
848 int argc;
849 char *argv[];
850 {
851
852 code = togglevar(argc, argv, &verbose, "Verbose mode");
853 }
854
855 /*
856 * Toggle PORT/LPRT cmd use before each data connection.
857 */
858 /*VARARGS*/
859 void
860 setport(argc, argv)
861 int argc;
862 char *argv[];
863 {
864
865 code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds");
866 }
867
868 /*
869 * Toggle transfer progress bar.
870 */
871 /*VARARGS*/
872 void
873 setprogress(argc, argv)
874 int argc;
875 char *argv[];
876 {
877
878 code = togglevar(argc, argv, &progress, "Progress bar");
879 if (progress)
880 hash = 0;
881 }
882
883 /*
884 * Turn on interactive prompting during mget, mput, and mdelete.
885 */
886 /*VARARGS*/
887 void
888 setprompt(argc, argv)
889 int argc;
890 char *argv[];
891 {
892
893 code = togglevar(argc, argv, &interactive, "Interactive mode");
894 }
895
896 /*
897 * Toggle gate-ftp mode, or set gate-ftp server
898 */
899 /*VARARGS*/
900 void
901 setgate(argc, argv)
902 int argc;
903 char *argv[];
904 {
905 static char gsbuf[MAXHOSTNAMELEN];
906
907 if (argc > 3) {
908 fprintf(ttyout,
909 "usage: %s [ on | off | gateserver [ port ] ]\n", argv[0]);
910 code = -1;
911 return;
912 } else if (argc < 2) {
913 gatemode = !gatemode;
914 } else {
915 if (argc == 2 && strcasecmp(argv[1], "on") == 0)
916 gatemode = 1;
917 else if (argc == 2 && strcasecmp(argv[1], "off") == 0)
918 gatemode = 0;
919 else {
920 if (argc == 3)
921 gateport = strdup(argv[2]);
922 (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf));
923 gateserver = gsbuf;
924 gatemode = 1;
925 }
926 }
927 if (gatemode && (gateserver == NULL || *gateserver == '\0')) {
928 fprintf(ttyout,
929 "Disabling gate-ftp mode - no gate-ftp server defined.\n");
930 gatemode = 0;
931 } else {
932 fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n",
933 onoff(gatemode), *gateserver ? gateserver : "(none)",
934 gateport);
935 }
936 code = gatemode;
937 }
938
939 /*
940 * Toggle metacharacter interpretation on local file names.
941 */
942 /*VARARGS*/
943 void
944 setglob(argc, argv)
945 int argc;
946 char *argv[];
947 {
948
949 code = togglevar(argc, argv, &doglob, "Globbing");
950 }
951
952 /*
953 * Toggle preserving modification times on retrieved files.
954 */
955 /*VARARGS*/
956 void
957 setpreserve(argc, argv)
958 int argc;
959 char *argv[];
960 {
961
962 code = togglevar(argc, argv, &preserve, "Preserve modification times");
963 }
964
965 /*
966 * Set debugging mode on/off and/or set level of debugging.
967 */
968 /*VARARGS*/
969 void
970 setdebug(argc, argv)
971 int argc;
972 char *argv[];
973 {
974 if (argc > 2) {
975 fprintf(ttyout, "usage: %s [ on | off | debuglevel ]\n",
976 argv[0]);
977 code = -1;
978 return;
979 } else if (argc == 2) {
980 if (strcasecmp(argv[1], "on") == 0)
981 debug = 1;
982 else if (strcasecmp(argv[1], "off") == 0)
983 debug = 0;
984 else {
985 int val;
986
987 val = strsuftoi(argv[1]);
988 if (val < 0) {
989 fprintf(ttyout, "%s: bad debugging value.\n",
990 argv[1]);
991 code = -1;
992 return;
993 }
994 debug = val;
995 }
996 } else
997 debug = !debug;
998 if (debug)
999 options |= SO_DEBUG;
1000 else
1001 options &= ~SO_DEBUG;
1002 fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug);
1003 code = debug > 0;
1004 }
1005
1006 /*
1007 * Set current working directory on remote machine.
1008 */
1009 void
1010 cd(argc, argv)
1011 int argc;
1012 char *argv[];
1013 {
1014 int r;
1015
1016 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) ||
1017 argc > 2) {
1018 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]);
1019 code = -1;
1020 return;
1021 }
1022 r = command("CWD %s", argv[1]);
1023 if (r == ERROR && code == 500) {
1024 if (verbose)
1025 fputs("CWD command not recognized, trying XCWD.\n",
1026 ttyout);
1027 r = command("XCWD %s", argv[1]);
1028 }
1029 if (r == COMPLETE)
1030 dirchange = 1;
1031 }
1032
1033 /*
1034 * Set current working directory on local machine.
1035 */
1036 void
1037 lcd(argc, argv)
1038 int argc;
1039 char *argv[];
1040 {
1041 char buf[MAXPATHLEN];
1042 char *locdir;
1043
1044 if (argc < 2) {
1045 argc++;
1046 argv[1] = home;
1047 }
1048 if (argc != 2) {
1049 fprintf(ttyout, "usage: %s local-directory\n", argv[0]);
1050 code = -1;
1051 return;
1052 }
1053 if ((locdir = globulize(argv[1])) == NULL) {
1054 code = -1;
1055 return;
1056 }
1057 if (chdir(locdir) < 0) {
1058 warn("local: %s", locdir);
1059 code = -1;
1060 } else {
1061 if (getcwd(buf, sizeof(buf)) != NULL)
1062 fprintf(ttyout, "Local directory now %s\n", buf);
1063 else
1064 warn("getcwd: %s", locdir);
1065 code = 0;
1066 }
1067 (void)free(locdir);
1068 }
1069
1070 /*
1071 * Delete a single file.
1072 */
1073 void
1074 delete(argc, argv)
1075 int argc;
1076 char *argv[];
1077 {
1078
1079 if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) {
1080 fprintf(ttyout, "usage: %s remote-file\n", argv[0]);
1081 code = -1;
1082 return;
1083 }
1084 (void)command("DELE %s", argv[1]);
1085 }
1086
1087 /*
1088 * Delete multiple files.
1089 */
1090 void
1091 mdelete(argc, argv)
1092 int argc;
1093 char *argv[];
1094 {
1095 sig_t oldintr;
1096 int ointer;
1097 char *cp;
1098
1099 if (argc < 2 && !another(&argc, &argv, "remote-files")) {
1100 fprintf(ttyout, "usage: %s remote-files\n", argv[0]);
1101 code = -1;
1102 return;
1103 }
1104 mname = argv[0];
1105 mflag = 1;
1106 oldintr = xsignal(SIGINT, mabort);
1107 (void)setjmp(jabort);
1108 while ((cp = remglob(argv, 0, NULL)) != NULL) {
1109 if (*cp == '\0') {
1110 mflag = 0;
1111 continue;
1112 }
1113 if (mflag && confirm(argv[0], cp)) {
1114 (void)command("DELE %s", cp);
1115 if (!mflag && fromatty) {
1116 ointer = interactive;
1117 interactive = 1;
1118 if (confirm("Continue with", "mdelete")) {
1119 mflag++;
1120 }
1121 interactive = ointer;
1122 }
1123 }
1124 }
1125 (void)xsignal(SIGINT, oldintr);
1126 mflag = 0;
1127 }
1128
1129 /*
1130 * Rename a remote file.
1131 */
1132 void
1133 renamefile(argc, argv)
1134 int argc;
1135 char *argv[];
1136 {
1137
1138 if (argc < 2 && !another(&argc, &argv, "from-name"))
1139 goto usage;
1140 if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) {
1141 usage:
1142 fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]);
1143 code = -1;
1144 return;
1145 }
1146 if (command("RNFR %s", argv[1]) == CONTINUE)
1147 (void)command("RNTO %s", argv[2]);
1148 }
1149
1150 /*
1151 * Get a directory listing of remote files.
1152 */
1153 void
1154 ls(argc, argv)
1155 int argc;
1156 char *argv[];
1157 {
1158 const char *cmd;
1159 char *remdir, *locfile;
1160 int freelocfile, pagecmd;
1161
1162 remdir = NULL;
1163 locfile = "-";
1164 freelocfile = pagecmd = 0;
1165 /*
1166 * assume all `pager' versions of the commands
1167 * are the only ones that start with `p'
1168 */
1169 if (argv[0][0] == 'p')
1170 pagecmd = 1;
1171
1172 cmd = "NLST";
1173 if (strcmp(argv[0] + pagecmd, "dir") == 0)
1174 cmd = "LIST";
1175
1176 if (argc > 1)
1177 remdir = argv[1];
1178 if (argc > 2)
1179 locfile = argv[2];
1180 if (argc > 3 || (pagecmd && argc > 2)) {
1181 if (pagecmd)
1182 fprintf(ttyout,
1183 "usage: %s [remote-directory]\n", argv[0]);
1184 else
1185 fprintf(ttyout,
1186 "usage: %s [remote-directory [local-file]]\n",
1187 argv[0]);
1188 code = -1;
1189 goto freels;
1190 }
1191
1192 if (pagecmd) {
1193 char *p;
1194 int len;
1195
1196 p = getenv("PAGER");
1197 if (p == NULL || p[0] == '\0')
1198 p = PAGER;
1199 len = strlen(p) + 2;
1200 locfile = xmalloc(len);
1201 locfile[0] = '|';
1202 (void)strlcpy(locfile + 1, p, len - 1);
1203 freelocfile = 1;
1204 } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') {
1205 if ((locfile = globulize(locfile)) == NULL ||
1206 !confirm("output to local-file:", locfile)) {
1207 code = -1;
1208 goto freels;
1209 }
1210 freelocfile = 1;
1211 }
1212 recvrequest(cmd, locfile, remdir, "w", 0, 0);
1213 freels:
1214 if (freelocfile && locfile)
1215 (void)free(locfile);
1216 }
1217
1218 /*
1219 * Get a directory listing of multiple remote files.
1220 */
1221 void
1222 mls(argc, argv)
1223 int argc;
1224 char *argv[];
1225 {
1226 sig_t oldintr;
1227 int ointer, i;
1228 int dolist;
1229 char mode[1], *dest, *odest;
1230
1231 if (argc < 2 && !another(&argc, &argv, "remote-files"))
1232 goto usage;
1233 if (argc < 3 && !another(&argc, &argv, "local-file")) {
1234 usage:
1235 fprintf(ttyout, "usage: %s remote-files local-file\n", argv[0]);
1236 code = -1;
1237 return;
1238 }
1239 odest = dest = argv[argc - 1];
1240 argv[argc - 1] = NULL;
1241 if (strcmp(dest, "-") && *dest != '|')
1242 if (((dest = globulize(dest)) == NULL) ||
1243 !confirm("output to local-file:", dest)) {
1244 code = -1;
1245 return;
1246 }
1247 dolist = strcmp(argv[0], "mls");
1248 mname = argv[0];
1249 mflag = 1;
1250 oldintr = xsignal(SIGINT, mabort);
1251 (void)setjmp(jabort);
1252 for (i = 1; mflag && i < argc-1; ++i) {
1253 *mode = (i == 1) ? 'w' : 'a';
1254 recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], mode,
1255 0, 0);
1256 if (!mflag && fromatty) {
1257 ointer = interactive;
1258 interactive = 1;
1259 if (confirm("Continue with", argv[0])) {
1260 mflag ++;
1261 }
1262 interactive = ointer;
1263 }
1264 }
1265 (void)xsignal(SIGINT, oldintr);
1266 mflag = 0;
1267 if (dest != odest) /* free up after globulize() */
1268 free(dest);
1269 }
1270
1271 /*
1272 * Do a shell escape
1273 */
1274 /*ARGSUSED*/
1275 void
1276 shell(argc, argv)
1277 int argc;
1278 char *argv[];
1279 {
1280 pid_t pid;
1281 sig_t old1, old2;
1282 char shellnam[MAXPATHLEN], *shell, *namep;
1283 int wait_status;
1284
1285 old1 = xsignal(SIGINT, SIG_IGN);
1286 old2 = xsignal(SIGQUIT, SIG_IGN);
1287 if ((pid = fork()) == 0) {
1288 for (pid = 3; pid < 20; pid++)
1289 (void)close(pid);
1290 (void)xsignal(SIGINT, SIG_DFL);
1291 (void)xsignal(SIGQUIT, SIG_DFL);
1292 shell = getenv("SHELL");
1293 if (shell == NULL)
1294 shell = _PATH_BSHELL;
1295 namep = strrchr(shell, '/');
1296 if (namep == NULL)
1297 namep = shell;
1298 shellnam[0] = '-';
1299 (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1);
1300 if (strcmp(namep, "sh") != 0)
1301 shellnam[0] = '+';
1302 if (debug) {
1303 fputs(shell, ttyout);
1304 putc('\n', ttyout);
1305 }
1306 if (argc > 1) {
1307 execl(shell, shellnam, "-c", altarg, (char *)0);
1308 }
1309 else {
1310 execl(shell, shellnam, (char *)0);
1311 }
1312 warn("%s", shell);
1313 code = -1;
1314 exit(1);
1315 }
1316 if (pid > 0)
1317 while (wait(&wait_status) != pid)
1318 ;
1319 (void)xsignal(SIGINT, old1);
1320 (void)xsignal(SIGQUIT, old2);
1321 if (pid == -1) {
1322 warn("Try again later");
1323 code = -1;
1324 }
1325 else {
1326 code = 0;
1327 }
1328 }
1329
1330 /*
1331 * Send new user information (re-login)
1332 */
1333 void
1334 user(argc, argv)
1335 int argc;
1336 char *argv[];
1337 {
1338 char acct[80];
1339 int n, aflag = 0;
1340
1341 if (argc < 2)
1342 (void)another(&argc, &argv, "username");
1343 if (argc < 2 || argc > 4) {
1344 fprintf(ttyout, "usage: %s username [password] [account]\n",
1345 argv[0]);
1346 code = -1;
1347 return;
1348 }
1349 n = command("USER %s", argv[1]);
1350 if (n == CONTINUE) {
1351 if (argc < 3) {
1352 argv[2] = getpass("Password: ");
1353 argc++;
1354 }
1355 n = command("PASS %s", argv[2]);
1356 }
1357 if (n == CONTINUE) {
1358 if (argc < 4) {
1359 (void)fputs("Account: ", ttyout);
1360 (void)fflush(ttyout);
1361 if (fgets(acct, sizeof(acct) - 1, stdin) == NULL) {
1362 fprintf(ttyout,
1363 "\nEOF received; login aborted.\n");
1364 code = -1;
1365 return;
1366 }
1367 acct[strlen(acct) - 1] = '\0';
1368 argv[3] = acct; argc++;
1369 }
1370 n = command("ACCT %s", argv[3]);
1371 aflag++;
1372 }
1373 if (n != COMPLETE) {
1374 fputs("Login failed.\n", ttyout);
1375 return;
1376 }
1377 if (!aflag && argc == 4) {
1378 (void)command("ACCT %s", argv[3]);
1379 }
1380 connected = -1;
1381 }
1382
1383 /*
1384 * Print working directory on remote machine.
1385 */
1386 /*VARARGS*/
1387 void
1388 pwd(argc, argv)
1389 int argc;
1390 char *argv[];
1391 {
1392 int oldverbose = verbose;
1393
1394 /*
1395 * If we aren't verbose, this doesn't do anything!
1396 */
1397 verbose = 1;
1398 if (command("PWD") == ERROR && code == 500) {
1399 fputs("PWD command not recognized, trying XPWD.\n", ttyout);
1400 (void)command("XPWD");
1401 }
1402 verbose = oldverbose;
1403 }
1404
1405 /*
1406 * Print working directory on local machine.
1407 */
1408 void
1409 lpwd(argc, argv)
1410 int argc;
1411 char *argv[];
1412 {
1413 char buf[MAXPATHLEN];
1414
1415 if (getcwd(buf, sizeof(buf)) != NULL)
1416 fprintf(ttyout, "Local directory %s\n", buf);
1417 else
1418 warn("getcwd");
1419 code = 0;
1420 }
1421
1422 /*
1423 * Make a directory.
1424 */
1425 void
1426 makedir(argc, argv)
1427 int argc;
1428 char *argv[];
1429 {
1430
1431 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1432 argc > 2) {
1433 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1434 code = -1;
1435 return;
1436 }
1437 if (command("MKD %s", argv[1]) == ERROR && code == 500) {
1438 if (verbose)
1439 fputs("MKD command not recognized, trying XMKD.\n",
1440 ttyout);
1441 (void)command("XMKD %s", argv[1]);
1442 }
1443 }
1444
1445 /*
1446 * Remove a directory.
1447 */
1448 void
1449 removedir(argc, argv)
1450 int argc;
1451 char *argv[];
1452 {
1453
1454 if ((argc < 2 && !another(&argc, &argv, "directory-name")) ||
1455 argc > 2) {
1456 fprintf(ttyout, "usage: %s directory-name\n", argv[0]);
1457 code = -1;
1458 return;
1459 }
1460 if (command("RMD %s", argv[1]) == ERROR && code == 500) {
1461 if (verbose)
1462 fputs("RMD command not recognized, trying XRMD.\n",
1463 ttyout);
1464 (void)command("XRMD %s", argv[1]);
1465 }
1466 }
1467
1468 /*
1469 * Send a line, verbatim, to the remote machine.
1470 */
1471 void
1472 quote(argc, argv)
1473 int argc;
1474 char *argv[];
1475 {
1476
1477 if (argc < 2 && !another(&argc, &argv, "command line to send")) {
1478 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1479 code = -1;
1480 return;
1481 }
1482 quote1("", argc, argv);
1483 }
1484
1485 /*
1486 * Send a SITE command to the remote machine. The line
1487 * is sent verbatim to the remote machine, except that the
1488 * word "SITE" is added at the front.
1489 */
1490 void
1491 site(argc, argv)
1492 int argc;
1493 char *argv[];
1494 {
1495
1496 if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
1497 fprintf(ttyout, "usage: %s line-to-send\n", argv[0]);
1498 code = -1;
1499 return;
1500 }
1501 quote1("SITE ", argc, argv);
1502 }
1503
1504 /*
1505 * Turn argv[1..argc) into a space-separated string, then prepend initial text.
1506 * Send the result as a one-line command and get response.
1507 */
1508 void
1509 quote1(initial, argc, argv)
1510 const char *initial;
1511 int argc;
1512 char *argv[];
1513 {
1514 int i;
1515 char buf[BUFSIZ]; /* must be >= sizeof(line) */
1516
1517 (void)strlcpy(buf, initial, sizeof(buf));
1518 for (i = 1; i < argc; i++) {
1519 (void)strlcat(buf, argv[i], sizeof(buf));
1520 if (i < (argc - 1))
1521 (void)strlcat(buf, " ", sizeof(buf));
1522 }
1523 if (command("%s", buf) == PRELIM) {
1524 while (getreply(0) == PRELIM)
1525 continue;
1526 }
1527 }
1528
1529 void
1530 do_chmod(argc, argv)
1531 int argc;
1532 char *argv[];
1533 {
1534
1535 if (argc < 2 && !another(&argc, &argv, "mode"))
1536 goto usage;
1537 if ((argc < 3 && !another(&argc, &argv, "file-name")) || argc > 3) {
1538 usage:
1539 fprintf(ttyout, "usage: %s mode file-name\n", argv[0]);
1540 code = -1;
1541 return;
1542 }
1543 (void)command("SITE CHMOD %s %s", argv[1], argv[2]);
1544 }
1545
1546 void
1547 do_umask(argc, argv)
1548 int argc;
1549 char *argv[];
1550 {
1551 int oldverbose = verbose;
1552
1553 verbose = 1;
1554 (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
1555 verbose = oldverbose;
1556 }
1557
1558 void
1559 idlecmd(argc, argv)
1560 int argc;
1561 char *argv[];
1562 {
1563 int oldverbose = verbose;
1564
1565 verbose = 1;
1566 (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
1567 verbose = oldverbose;
1568 }
1569
1570 /*
1571 * Ask the other side for help.
1572 */
1573 void
1574 rmthelp(argc, argv)
1575 int argc;
1576 char *argv[];
1577 {
1578 int oldverbose = verbose;
1579
1580 verbose = 1;
1581 (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
1582 verbose = oldverbose;
1583 }
1584
1585 /*
1586 * Terminate session and exit.
1587 */
1588 /*VARARGS*/
1589 void
1590 quit(argc, argv)
1591 int argc;
1592 char *argv[];
1593 {
1594
1595 if (connected)
1596 disconnect(0, 0);
1597 pswitch(1);
1598 if (connected) {
1599 disconnect(0, 0);
1600 }
1601 exit(0);
1602 }
1603
1604 /*
1605 * Terminate session, but don't exit.
1606 */
1607 void
1608 disconnect(argc, argv)
1609 int argc;
1610 char *argv[];
1611 {
1612
1613 if (!connected)
1614 return;
1615 (void)command("QUIT");
1616 if (cout) {
1617 (void)fclose(cout);
1618 }
1619 cout = NULL;
1620 connected = 0;
1621 data = -1;
1622 if (!proxy) {
1623 macnum = 0;
1624 }
1625 }
1626
1627 void
1628 account(argc, argv)
1629 int argc;
1630 char *argv[];
1631 {
1632 char *ap;
1633
1634 if (argc > 2) {
1635 fprintf(ttyout, "usage: %s [password]\n", argv[0]);
1636 code = -1;
1637 return;
1638 }
1639 else if (argc == 2)
1640 ap = argv[1];
1641 else
1642 ap = getpass("Account:");
1643 (void)command("ACCT %s", ap);
1644 }
1645
1646 jmp_buf abortprox;
1647
1648 void
1649 proxabort(notused)
1650 int notused;
1651 {
1652
1653 alarmtimer(0);
1654 if (!proxy) {
1655 pswitch(1);
1656 }
1657 if (connected) {
1658 proxflag = 1;
1659 }
1660 else {
1661 proxflag = 0;
1662 }
1663 pswitch(0);
1664 longjmp(abortprox, 1);
1665 }
1666
1667 void
1668 doproxy(argc, argv)
1669 int argc;
1670 char *argv[];
1671 {
1672 struct cmd *c;
1673 int cmdpos;
1674 sig_t oldintr;
1675
1676 if (argc < 2 && !another(&argc, &argv, "command")) {
1677 fprintf(ttyout, "usage: %s command\n", argv[0]);
1678 code = -1;
1679 return;
1680 }
1681 c = getcmd(argv[1]);
1682 if (c == (struct cmd *) -1) {
1683 fputs("?Ambiguous command.\n", ttyout);
1684 code = -1;
1685 return;
1686 }
1687 if (c == 0) {
1688 fputs("?Invalid command.\n", ttyout);
1689 code = -1;
1690 return;
1691 }
1692 if (!c->c_proxy) {
1693 fputs("?Invalid proxy command.\n", ttyout);
1694 code = -1;
1695 return;
1696 }
1697 if (setjmp(abortprox)) {
1698 code = -1;
1699 return;
1700 }
1701 oldintr = xsignal(SIGINT, proxabort);
1702 pswitch(1);
1703 if (c->c_conn && !connected) {
1704 fputs("Not connected.\n", ttyout);
1705 pswitch(0);
1706 (void)xsignal(SIGINT, oldintr);
1707 code = -1;
1708 return;
1709 }
1710 cmdpos = strcspn(line, " \t");
1711 if (cmdpos > 0) /* remove leading "proxy " from input buffer */
1712 memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1);
1713 (*c->c_handler)(argc-1, argv+1);
1714 if (connected) {
1715 proxflag = 1;
1716 }
1717 else {
1718 proxflag = 0;
1719 }
1720 pswitch(0);
1721 (void)xsignal(SIGINT, oldintr);
1722 }
1723
1724 void
1725 setcase(argc, argv)
1726 int argc;
1727 char *argv[];
1728 {
1729
1730 code = togglevar(argc, argv, &mcase, "Case mapping");
1731 }
1732
1733 /*
1734 * convert the given name to lower case if it's all upper case, into
1735 * a static buffer which is returned to the caller
1736 */
1737 char *
1738 docase(name)
1739 char *name;
1740 {
1741 static char new[MAXPATHLEN];
1742 int i, dochange;
1743
1744 dochange = 1;
1745 for (i = 0; name[i] != '\0' && i < sizeof(new) - 1; i++) {
1746 new[i] = name[i];
1747 if (islower((unsigned char)new[i]))
1748 dochange = 0;
1749 }
1750 new[i] = '\0';
1751
1752 if (dochange) {
1753 for (i = 0; new[i] != '\0'; i++)
1754 if (isupper((unsigned char)new[i]))
1755 new[i] = tolower(new[i]);
1756 }
1757 return (new);
1758 }
1759
1760 void
1761 setcr(argc, argv)
1762 int argc;
1763 char *argv[];
1764 {
1765
1766 code = togglevar(argc, argv, &crflag, "Carriage Return stripping");
1767 }
1768
1769 void
1770 setntrans(argc, argv)
1771 int argc;
1772 char *argv[];
1773 {
1774 if (argc == 1) {
1775 ntflag = 0;
1776 fputs("Ntrans off.\n", ttyout);
1777 code = ntflag;
1778 return;
1779 }
1780 ntflag++;
1781 code = ntflag;
1782 (void)strlcpy(ntin, argv[1], sizeof(ntin));
1783 if (argc == 2) {
1784 ntout[0] = '\0';
1785 return;
1786 }
1787 (void)strlcpy(ntout, argv[2], sizeof(ntout));
1788 }
1789
1790 char *
1791 dotrans(name)
1792 char *name;
1793 {
1794 static char new[MAXPATHLEN];
1795 char *cp1, *cp2 = new;
1796 int i, ostop, found;
1797
1798 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
1799 continue;
1800 for (cp1 = name; *cp1; cp1++) {
1801 found = 0;
1802 for (i = 0; *(ntin + i) && i < 16; i++) {
1803 if (*cp1 == *(ntin + i)) {
1804 found++;
1805 if (i < ostop) {
1806 *cp2++ = *(ntout + i);
1807 }
1808 break;
1809 }
1810 }
1811 if (!found) {
1812 *cp2++ = *cp1;
1813 }
1814 }
1815 *cp2 = '\0';
1816 return (new);
1817 }
1818
1819 void
1820 setnmap(argc, argv)
1821 int argc;
1822 char *argv[];
1823 {
1824 char *cp;
1825
1826 if (argc == 1) {
1827 mapflag = 0;
1828 fputs("Nmap off.\n", ttyout);
1829 code = mapflag;
1830 return;
1831 }
1832 if ((argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) {
1833 fprintf(ttyout, "usage: %s [mapin mapout]\n", argv[0]);
1834 code = -1;
1835 return;
1836 }
1837 mapflag = 1;
1838 code = 1;
1839 cp = strchr(altarg, ' ');
1840 if (proxy) {
1841 while(*++cp == ' ')
1842 continue;
1843 altarg = cp;
1844 cp = strchr(altarg, ' ');
1845 }
1846 *cp = '\0';
1847 (void)strlcpy(mapin, altarg, MAXPATHLEN);
1848 while (*++cp == ' ')
1849 continue;
1850 (void)strlcpy(mapout, cp, MAXPATHLEN);
1851 }
1852
1853 char *
1854 domap(name)
1855 char *name;
1856 {
1857 static char new[MAXPATHLEN];
1858 char *cp1 = name, *cp2 = mapin;
1859 char *tp[9], *te[9];
1860 int i, toks[9], toknum = 0, match = 1;
1861
1862 for (i=0; i < 9; ++i) {
1863 toks[i] = 0;
1864 }
1865 while (match && *cp1 && *cp2) {
1866 switch (*cp2) {
1867 case '\\':
1868 if (*++cp2 != *cp1) {
1869 match = 0;
1870 }
1871 break;
1872 case '$':
1873 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
1874 if (*cp1 != *(++cp2+1)) {
1875 toks[toknum = *cp2 - '1']++;
1876 tp[toknum] = cp1;
1877 while (*++cp1 && *(cp2+1)
1878 != *cp1);
1879 te[toknum] = cp1;
1880 }
1881 cp2++;
1882 break;
1883 }
1884 /* FALLTHROUGH */
1885 default:
1886 if (*cp2 != *cp1) {
1887 match = 0;
1888 }
1889 break;
1890 }
1891 if (match && *cp1) {
1892 cp1++;
1893 }
1894 if (match && *cp2) {
1895 cp2++;
1896 }
1897 }
1898 if (!match && *cp1) /* last token mismatch */
1899 {
1900 toks[toknum] = 0;
1901 }
1902 cp1 = new;
1903 *cp1 = '\0';
1904 cp2 = mapout;
1905 while (*cp2) {
1906 match = 0;
1907 switch (*cp2) {
1908 case '\\':
1909 if (*(cp2 + 1)) {
1910 *cp1++ = *++cp2;
1911 }
1912 break;
1913 case '[':
1914 LOOP:
1915 if (*++cp2 == '$' &&
1916 isdigit((unsigned char)*(cp2+1))) {
1917 if (*++cp2 == '0') {
1918 char *cp3 = name;
1919
1920 while (*cp3) {
1921 *cp1++ = *cp3++;
1922 }
1923 match = 1;
1924 }
1925 else if (toks[toknum = *cp2 - '1']) {
1926 char *cp3 = tp[toknum];
1927
1928 while (cp3 != te[toknum]) {
1929 *cp1++ = *cp3++;
1930 }
1931 match = 1;
1932 }
1933 }
1934 else {
1935 while (*cp2 && *cp2 != ',' &&
1936 *cp2 != ']') {
1937 if (*cp2 == '\\') {
1938 cp2++;
1939 }
1940 else if (*cp2 == '$' &&
1941 isdigit((unsigned char)*(cp2+1))) {
1942 if (*++cp2 == '0') {
1943 char *cp3 = name;
1944
1945 while (*cp3) {
1946 *cp1++ = *cp3++;
1947 }
1948 }
1949 else if (toks[toknum =
1950 *cp2 - '1']) {
1951 char *cp3=tp[toknum];
1952
1953 while (cp3 !=
1954 te[toknum]) {
1955 *cp1++ = *cp3++;
1956 }
1957 }
1958 }
1959 else if (*cp2) {
1960 *cp1++ = *cp2++;
1961 }
1962 }
1963 if (!*cp2) {
1964 fputs(
1965 "nmap: unbalanced brackets.\n",
1966 ttyout);
1967 return (name);
1968 }
1969 match = 1;
1970 cp2--;
1971 }
1972 if (match) {
1973 while (*++cp2 && *cp2 != ']') {
1974 if (*cp2 == '\\' && *(cp2 + 1)) {
1975 cp2++;
1976 }
1977 }
1978 if (!*cp2) {
1979 fputs(
1980 "nmap: unbalanced brackets.\n",
1981 ttyout);
1982 return (name);
1983 }
1984 break;
1985 }
1986 switch (*++cp2) {
1987 case ',':
1988 goto LOOP;
1989 case ']':
1990 break;
1991 default:
1992 cp2--;
1993 goto LOOP;
1994 }
1995 break;
1996 case '$':
1997 if (isdigit((unsigned char)*(cp2 + 1))) {
1998 if (*++cp2 == '0') {
1999 char *cp3 = name;
2000
2001 while (*cp3) {
2002 *cp1++ = *cp3++;
2003 }
2004 }
2005 else if (toks[toknum = *cp2 - '1']) {
2006 char *cp3 = tp[toknum];
2007
2008 while (cp3 != te[toknum]) {
2009 *cp1++ = *cp3++;
2010 }
2011 }
2012 break;
2013 }
2014 /* intentional drop through */
2015 default:
2016 *cp1++ = *cp2;
2017 break;
2018 }
2019 cp2++;
2020 }
2021 *cp1 = '\0';
2022 if (!*new) {
2023 return (name);
2024 }
2025 return (new);
2026 }
2027
2028 void
2029 setpassive(argc, argv)
2030 int argc;
2031 char *argv[];
2032 {
2033
2034 code = togglevar(argc, argv, &passivemode,
2035 verbose ? "Passive mode" : NULL);
2036 }
2037
2038 void
2039 setsunique(argc, argv)
2040 int argc;
2041 char *argv[];
2042 {
2043
2044 code = togglevar(argc, argv, &sunique, "Store unique");
2045 }
2046
2047 void
2048 setrunique(argc, argv)
2049 int argc;
2050 char *argv[];
2051 {
2052
2053 code = togglevar(argc, argv, &runique, "Receive unique");
2054 }
2055
2056 int
2057 parserate(argc, argv, cmdlineopt)
2058 int argc;
2059 char *argv[];
2060 int cmdlineopt;
2061 {
2062 int dir, max, incr, showonly;
2063 sig_t oldusr1, oldusr2;
2064
2065 if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) {
2066 usage:
2067 if (cmdlineopt)
2068 fprintf(ttyout,
2069 "usage: %s (all|get|put),maximum[,increment]]\n",
2070 argv[0]);
2071 else
2072 fprintf(ttyout,
2073 "usage: %s (all|get|put) [maximum [increment]]\n",
2074 argv[0]);
2075 return -1;
2076 }
2077 dir = max = incr = showonly = 0;
2078 #define RATE_GET 1
2079 #define RATE_PUT 2
2080 #define RATE_ALL (RATE_GET | RATE_PUT)
2081
2082 if (strcasecmp(argv[1], "all") == 0)
2083 dir = RATE_ALL;
2084 else if (strcasecmp(argv[1], "get") == 0)
2085 dir = RATE_GET;
2086 else if (strcasecmp(argv[1], "put") == 0)
2087 dir = RATE_PUT;
2088 else
2089 goto usage;
2090
2091 if (argc >= 3) {
2092 if ((max = strsuftoi(argv[2])) < 0)
2093 goto usage;
2094 } else
2095 showonly = 1;
2096
2097 if (argc == 4) {
2098 if ((incr = strsuftoi(argv[3])) <= 0)
2099 goto usage;
2100 } else
2101 incr = DEFAULTINCR;
2102
2103 oldusr1 = xsignal(SIGUSR1, SIG_IGN);
2104 oldusr2 = xsignal(SIGUSR2, SIG_IGN);
2105 if (dir & RATE_GET) {
2106 if (!showonly) {
2107 rate_get = max;
2108 rate_get_incr = incr;
2109 }
2110 if (!cmdlineopt || verbose)
2111 fprintf(ttyout,
2112 "Get transfer rate throttle: %s; maximum: %d; increment %d.\n",
2113 onoff(rate_get), rate_get, rate_get_incr);
2114 }
2115 if (dir & RATE_PUT) {
2116 if (!showonly) {
2117 rate_put = max;
2118 rate_put_incr = incr;
2119 }
2120 if (!cmdlineopt || verbose)
2121 fprintf(ttyout,
2122 "Put transfer rate throttle: %s; maximum: %d; increment %d.\n",
2123 onoff(rate_put), rate_put, rate_put_incr);
2124 }
2125 (void)xsignal(SIGUSR1, oldusr1);
2126 (void)xsignal(SIGUSR2, oldusr2);
2127 return 0;
2128 }
2129
2130 void
2131 setrate(argc, argv)
2132 int argc;
2133 char *argv[];
2134 {
2135
2136 code = parserate(argc, argv, 0);
2137 }
2138
2139 /* change directory to parent directory */
2140 void
2141 cdup(argc, argv)
2142 int argc;
2143 char *argv[];
2144 {
2145 int r;
2146
2147 r = command("CDUP");
2148 if (r == ERROR && code == 500) {
2149 if (verbose)
2150 fputs("CDUP command not recognized, trying XCUP.\n",
2151 ttyout);
2152 r = command("XCUP");
2153 }
2154 if (r == COMPLETE)
2155 dirchange = 1;
2156 }
2157
2158 /*
2159 * Restart transfer at specific point
2160 */
2161 void
2162 restart(argc, argv)
2163 int argc;
2164 char *argv[];
2165 {
2166
2167 if (argc > 2) {
2168 fprintf(ttyout, "usage: %s [restart_point]\n", argv[0]);
2169 code = -1;
2170 return;
2171 }
2172 if (argc == 2) {
2173 off_t rp;
2174 char *ep;
2175
2176 #ifndef NO_QUAD
2177 rp = strtoq(argv[1], &ep, 10);
2178 #else
2179 rp = strtol(argv[1], &ep, 10);
2180 #endif
2181 if (rp < 0 || *ep != '\0')
2182 fprintf(ttyout, "restart: Invalid offset `%s'\n",
2183 argv[1]);
2184 else
2185 restart_point = rp;
2186 }
2187 if (restart_point == 0)
2188 fputs("No restart point defined.\n", ttyout);
2189 else
2190 fprintf(ttyout,
2191 #ifndef NO_QUAD
2192 "Restarting at %lld for next get, put or append\n",
2193 (long long)restart_point);
2194 #else
2195 "Restarting at %ld for next get, put or append\n",
2196 (long)restart_point);
2197 #endif
2198 }
2199
2200 /*
2201 * Show remote system type
2202 */
2203 void
2204 syst(argc, argv)
2205 int argc;
2206 char *argv[];
2207 {
2208
2209 (void)command("SYST");
2210 }
2211
2212 void
2213 macdef(argc, argv)
2214 int argc;
2215 char *argv[];
2216 {
2217 char *tmp;
2218 int c;
2219
2220 if (macnum == 16) {
2221 fputs("Limit of 16 macros have already been defined.\n",
2222 ttyout);
2223 code = -1;
2224 return;
2225 }
2226 if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) {
2227 fprintf(ttyout, "usage: %s macro_name\n", argv[0]);
2228 code = -1;
2229 return;
2230 }
2231 if (interactive)
2232 fputs(
2233 "Enter macro line by line, terminating it with a null line.\n",
2234 ttyout);
2235 (void)strlcpy(macros[macnum].mac_name, argv[1],
2236 sizeof(macros[macnum].mac_name));
2237 if (macnum == 0)
2238 macros[macnum].mac_start = macbuf;
2239 else
2240 macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
2241 tmp = macros[macnum].mac_start;
2242 while (tmp != macbuf+4096) {
2243 if ((c = getchar()) == EOF) {
2244 fputs("macdef: end of file encountered.\n", ttyout);
2245 code = -1;
2246 return;
2247 }
2248 if ((*tmp = c) == '\n') {
2249 if (tmp == macros[macnum].mac_start) {
2250 macros[macnum++].mac_end = tmp;
2251 code = 0;
2252 return;
2253 }
2254 if (*(tmp-1) == '\0') {
2255 macros[macnum++].mac_end = tmp - 1;
2256 code = 0;
2257 return;
2258 }
2259 *tmp = '\0';
2260 }
2261 tmp++;
2262 }
2263 while (1) {
2264 while ((c = getchar()) != '\n' && c != EOF)
2265 /* LOOP */;
2266 if (c == EOF || getchar() == '\n') {
2267 fputs("Macro not defined - 4K buffer exceeded.\n",
2268 ttyout);
2269 code = -1;
2270 return;
2271 }
2272 }
2273 }
2274
2275 /*
2276 * Get size of file on remote machine
2277 */
2278 void
2279 sizecmd(argc, argv)
2280 int argc;
2281 char *argv[];
2282 {
2283 off_t size;
2284
2285 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2286 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2287 code = -1;
2288 return;
2289 }
2290 size = remotesize(argv[1], 1);
2291 if (size != -1)
2292 fprintf(ttyout,
2293 #ifndef NO_QUAD
2294 "%s\t%lld\n", argv[1], (long long)size);
2295 #else
2296 "%s\t%ld\n", argv[1], (long)size);
2297 #endif
2298 code = size;
2299 }
2300
2301 /*
2302 * Get last modification time of file on remote machine
2303 */
2304 void
2305 modtime(argc, argv)
2306 int argc;
2307 char *argv[];
2308 {
2309 time_t mtime;
2310
2311 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2312 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2313 code = -1;
2314 return;
2315 }
2316 mtime = remotemodtime(argv[1], 1);
2317 if (mtime != -1)
2318 fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime)));
2319 code = mtime;
2320 }
2321
2322 /*
2323 * Show status on remote machine
2324 */
2325 void
2326 rmtstatus(argc, argv)
2327 int argc;
2328 char *argv[];
2329 {
2330
2331 (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
2332 }
2333
2334 /*
2335 * Get file if modtime is more recent than current file
2336 */
2337 void
2338 newer(argc, argv)
2339 int argc;
2340 char *argv[];
2341 {
2342
2343 if (getit(argc, argv, -1, "w"))
2344 fprintf(ttyout,
2345 "Local file \"%s\" is newer than remote file \"%s\".\n",
2346 argv[2], argv[1]);
2347 }
2348
2349 /*
2350 * Display one local file through $PAGER (defaults to "more").
2351 */
2352 void
2353 lpage(argc, argv)
2354 int argc;
2355 char *argv[];
2356 {
2357 int len;
2358 char *p, *pager, *locfile;
2359
2360 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2361 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2362 code = -1;
2363 return;
2364 }
2365 if ((locfile = globulize(argv[1])) == NULL) {
2366 code = -1;
2367 return;
2368 }
2369 p = getenv("PAGER");
2370 if (p == NULL || p[0] == '\0')
2371 p = PAGER;
2372 len = strlen(p) + strlen(locfile) + 2;
2373 pager = xmalloc(len);
2374 (void)strlcpy(pager, p, len);
2375 (void)strlcat(pager, " ", len);
2376 (void)strlcat(pager, locfile, len);
2377 system(pager);
2378 (void)free(pager);
2379 (void)free(locfile);
2380 }
2381
2382 /*
2383 * Display one remote file through $PAGER (defaults to "more").
2384 */
2385 void
2386 page(argc, argv)
2387 int argc;
2388 char *argv[];
2389 {
2390 int ohash, orestart_point, overbose, len;
2391 char *p, *pager;
2392
2393 if ((argc < 2 && !another(&argc, &argv, "filename")) || argc > 2) {
2394 fprintf(ttyout, "usage: %s filename\n", argv[0]);
2395 code = -1;
2396 return;
2397 }
2398 p = getenv("PAGER");
2399 if (p == NULL || p[0] == '\0')
2400 p = PAGER;
2401 len = strlen(p) + 2;
2402 pager = xmalloc(len);
2403 pager[0] = '|';
2404 (void)strlcpy(pager + 1, p, len - 1);
2405
2406 ohash = hash;
2407 orestart_point = restart_point;
2408 overbose = verbose;
2409 hash = restart_point = verbose = 0;
2410 recvrequest("RETR", pager, argv[1], "r+w", 1, 0);
2411 hash = ohash;
2412 restart_point = orestart_point;
2413 verbose = overbose;
2414 (void)free(pager);
2415 }
2416
2417 /*
2418 * Set the socket send or receive buffer size.
2419 */
2420 void
2421 setxferbuf(argc, argv)
2422 int argc;
2423 char *argv[];
2424 {
2425 int size, dir;
2426
2427 if (argc != 2) {
2428 usage:
2429 fprintf(ttyout, "usage: %s size\n", argv[0]);
2430 code = -1;
2431 return;
2432 }
2433 if (strcasecmp(argv[0], "sndbuf") == 0)
2434 dir = RATE_PUT;
2435 else if (strcasecmp(argv[0], "rcvbuf") == 0)
2436 dir = RATE_GET;
2437 else if (strcasecmp(argv[0], "xferbuf") == 0)
2438 dir = RATE_ALL;
2439 else
2440 goto usage;
2441
2442 if ((size = strsuftoi(argv[1])) == -1)
2443 goto usage;
2444
2445 if (size == 0) {
2446 fprintf(ttyout, "%s: size must be positive.\n", argv[0]);
2447 goto usage;
2448 }
2449
2450 if (dir & RATE_PUT)
2451 sndbuf_size = size;
2452 if (dir & RATE_GET)
2453 rcvbuf_size = size;
2454 fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n",
2455 sndbuf_size, rcvbuf_size);
2456 }
2457
2458
2459 void
2460 setepsv4(argc, argv)
2461 int argc;
2462 char *argv[];
2463 {
2464
2465 code = togglevar(argc, argv, &epsv4,
2466 verbose ? "EPSV/EPRT on IPv4" : NULL);
2467 }
2468