diff options
author | Andrey A. Chernov <ache@FreeBSD.org> | 1994-09-22 23:45:37 +0000 |
---|---|---|
committer | Andrey A. Chernov <ache@FreeBSD.org> | 1994-09-22 23:45:37 +0000 |
commit | b7b69dce5b689dd1cddf5dbc778a125cc5afe296 (patch) | |
tree | 31cb31bfb69915fc791ce9c2ec7d345124bedebf |
ncftp 1.8.5vendor/ncftp/1.8.5
Notes
Notes:
svn path=/vendor/ncftp/dist/; revision=3004
svn path=/vendor/ncftp/1.8.5/; revision=3006; tag=vendor/ncftp/1.8.5
29 files changed, 12578 insertions, 0 deletions
diff --git a/usr.bin/ncftp/Blurb b/usr.bin/ncftp/Blurb new file mode 100644 index 000000000000..e9cd04c37e8d --- /dev/null +++ b/usr.bin/ncftp/Blurb @@ -0,0 +1,66 @@ +Subject: NcFTP 1.6.0 - Alternative User Interface for FTP + +Archive-name: ncftp/part01 +Environment: UNIX, ANSI-C, !SVR4 +Supersedes: ncftp: Volume 39, Issue 53-57 + +NcFTP - Alternative user interface for FTP +Version 1.6.0 by Mike Gleason, NCEMRSoft. + +I used to list the features of ncftp in this blurb, but there are just +too many to list. Even if you only ftp occasionally, it is worth your +time to install ncftp (or atleast bug your sysadmin to). If you won't take +my word for it, just ask around, or extract this archive and read the +man page. + +1.6.0 is an "evolutionary" upgrade, which consolidates all the previous +patches and adds a little. 1.6 and 1.5 are "interim" versions between +2.0 which has been suspended indefinitely due to time constraints. + +Major changes since 1.5.6: + +* Built-in support for the "term" package, used by Linux, etc. + +* SCO Xenix, Besta, AIX 2, AIX 3, Dynix/PTX support + +* Better ISC Unix support. + +* Several bug fixes. + + +Major changes since 1.0.2: + +* Supports the Getline (input-edit) and GNU Readline command-line + editing and history libraries. + +* Supports the Socks firewall library, and another firewall gateway + implementation. + +* Terrific new "recent-sites" file that automatically saves the + sites you call; when you open a site in the recent-sites file + (of course you can abbreviate the names), you start in the + same directory you were in last time. + +* Improved on-line help, and tips on how to use the program better + are printed each time you run the program. + +* Rewritten man page. + +* Faster ascii transfers. + +* Typing 'open' by itself lists all the sites the program knows + about (the ones in your .netrc and the recent-sites list) so + you can just pick one. + +* Enhanced colon-mode, that can dump a file to stdout (ftpcat mode) + or to a pager. (i.e. ncftp -c wu:/README >/dev/null). + +* You can choose whether an open is anonymous by default (like it + had always been) or a user login by default by setting a new + program variable. + +* Bugs fixed. + +Read the enclosed file, v2_Notes, which explains why I won't be +able to work on the nearly finished, and much improved v2.0. + diff --git a/usr.bin/ncftp/Makefile.ORIG b/usr.bin/ncftp/Makefile.ORIG new file mode 100644 index 000000000000..7fe52b8bdcfe --- /dev/null +++ b/usr.bin/ncftp/Makefile.ORIG @@ -0,0 +1,287 @@ +# Makefile for ncftp +# +# Major sections delimited by a dash lines. If several lines set the same +# make variable, you can choose between the commented #samples, or just +# type what you want manually. +#-------------------------------------------------------------------------- + + +# System dependent definitions. See the README, part B. +#-------------------------------------------------------------------------- +SDEFS = + + +# Program definitions. See the README, part C. +#-------------------------------------------------------------------------- +PDEFS = +#PDEFS = -DGETLINE +#PDEFS = -DREADLINE -DCURSES +#PDEFS = -DSOCKS +#PDEFS = -DPASSIVEMODE +#PDEFS = -DDEBUG -DDB_ERRS + + +# Choose your compiler and flags below. Make sure you use an ANSI compiler +# that handles new style function declarations and prototypes (gcc should). +#-------------------------------------------------------------------------- +CC = cc +#CC = gcc + +#CFLAGS = $(TERM_INC) -O +CFLAGS = $(TERM_INC) -O2 +#CFLAGS = $(TERM_INC) -g + +LFLAGS = -s +#LFLAGS = + + +# Additional libraries and/or object files. +# +# For each library, add -lLIBNAME to the LIBS line below, for a library +# named libLIBNAME.a. +# +# For each object file, just add the pathname of the object file. +# +# Some may need any of -lsocket, -lnet, -linet, -lintl, or -lnsl. +# You'll need -lcurses or -ltermcap if CURSES is defined. +# You'll need -lreadline AND either -lcurses or -ltermcap if you +# want to use the GNU Readline library. +# You'll need -lgetline (compile it as a library) if you want to use +# getline. +# If your system is running Yellow Pages, you'll need to add the library +# that has the YP/NIS version of getpwuid() in it (Important!) +# You'll need to know where the Rconnect.o object file is if you want +# to use Socks. +#-------------------------------------------------------------------------- +LIBS = +#LIBS = -ldbmalloc +#LIBS = -lgetline +#LIBS = -lreadline -lcurses +#LIBS = ../lib/Rconnect.o +#LIBS = -lnet -lnsl -lsocket -lcurses +#LIBS = -lcurses -ltermcap + +# If the libraries are in a non-standard directory, or you if want to use +# getline or readline and they aren't installed system-wide, add the +# extra directories to look in here, using -L's. +#-------------------------------------------------------------------------- +LIBDIRS = +#LIBDIRS = -L../getline +#LIBDIRS = -L../readline + +# To make term sources define this to your term directory +TERM_INC = +TERM_LIB = +#TERM_INC = -include /usr/local/include/termnet.h +#TERM_LIB = -ltermnet + +# Additional headers. +# +# If you defined READLINE or GETLINE, you have to tell where it's header +# file can be found. +# +# For READLINE, provide a path which would find <readline/readline.h>, +# so you would put the parent directory of the readline directory below. +# If you had '/usr/local/readline/readline.h' you would use +# -I/usr/local. +# +# For GETLINE, a little different. Just supply a path that would find +# <getline.h>. If you had '/usr/local/getline/getline.h' you would use +# -I/usr/local/getline. +#-------------------------------------------------------------------------- +HDRDIRS = +#HDRDIRS = -I../getline +#HDRDIRS = -I.. + + +# If you want to 'make install,' edit these variables, otherwise don't +# worry about it. +# To install MAN style pages, set MANDIR to the proper location. +# To install CATMAN style pages, set CATMANDIR, NROFF, and PACK to the proper +# locations. +# To inhibit the installation of either, unset MANDIR/CATMANDIR. +#-------------------------------------------------------------------------- +#BINDIR = /usr/lbin +BINDIR = /usr/local/bin +MANDIR = /usr/man/man1 +#MANDIR = +#CATMANDIR = /usr/catman/LOCAL/g1 +CATMANDIR = +NROFF = /usr/ucb/nroff +PACK = pack +TEST = test +RM = rm -f +CP = cp +CAT = cat + + +#************************************************ +#*** SHOULD NOT NEED TO EDIT BELOW THIS POINT *** +#************************************************ + +DEFS = $(PDEFS) $(SDEFS) +MK = $(CC) $(CFLAGS) $(DEFS) $(HDRDIRS) $(LFLAGS) $(LIBDIRS) $(LIBS) + +SRCS = cmds.c cmdtab.c ftp.c ftprc.c getpass.c glob.c main.c open.c set.c \ +tips.c util.c + +HEADERS = cmds.h copyright.h defaults.h ftp.h ftprc.h getpass.h glob.h \ +main.h open.h set.h sys.h util.h + +OBJS = cmds.o cmdtab.o ftp.o ftprc.o getpass.o glob.o main.o open.o set.o \ +tips.o util.o + +NAME = ncftp +MAN = ncftp.1 +CATMAN = ncftp.z +ALL = $(SRCS) $(HEADERS) patchlevel.h Blurb README Makefile $(MAN) \ +v2_Note + +C_COMPILE = $(CC) $(CFLAGS) $(DEFS) $(HDRDIRS) +C_COMPILE2 = $(CC) $(CFLAGS) $(DEFS) -DMK='"$(MK)"' $(HDRDIRS) + +all: $(NAME) done + +$(NAME): $(OBJS) + $(CC) $(LFLAGS) $(LIBDIRS) $(OBJS) -o $(NAME) $(LIBS) $(TERM_LIB) + +install: $(NAME) + if $(TEST) -f $(BINDIR)/term ; then \ + $(CP) $(BINDIR)/term $(BINDIR)/$(NAME) ; \ + $(CAT) $(NAME) > $(BINDIR)/$(NAME) ; \ + else \ + $(CP) $(NAME) $(BINDIR)/$(NAME) ; \ + fi + @if $(TEST) -n '$(MANDIR)'; then \ + $(MAKE) install_man ; else true ; fi + @if $(TEST) -n '$(CATMANDIR)'; then \ + $(MAKE) install_catman ; else true ; fi + +install_man: $(MAN) + $(CP) $(MAN) $(MANDIR)/$(MAN) + + +install_catman: $(CATMAN) + $(CP) $(CATMAN) $(CATMANDIR)/$(CATMAN) + +uninstall: + $(RM) $(BINDIR)/$(NAME) + $(RM) $(MANDIR)/$(MAN) + $(RM) $(CATMANDIR)/$(CATMAN) + +$(CATMAN): $(MAN) + $(RM) tmp + $(NROFF) -man -Tlp $(MAN) > tmp + $(PACK) -f tmp + mv tmp.z $(CATMAN) + +cmds.o: + $(C_COMPILE2) cmds.c -c + +.c.o: + $(C_COMPILE) -c $< + +done: $(NAME) + -@ls -l $(NAME) + -@echo 'Done.' + +clean: + rm -f $(OBJS) $(NAME) + +# Dependencies: +cmds.o: cmds.c +cmds.o: sys.h +cmds.o: util.h +cmds.o: cmds.h +cmds.o: main.h +cmds.o: ftp.h +cmds.o: ftprc.h +cmds.o: getpass.h +cmds.o: glob.h +cmds.o: open.h +cmds.o: set.h +cmds.o: defaults.h +cmds.o: copyright.h +cmdtab.o: cmdtab.c +cmdtab.o: sys.h +cmdtab.o: util.h +cmdtab.o: cmds.h +cmdtab.o: main.h +cmdtab.o: ftp.h +cmdtab.o: ftprc.h +cmdtab.o: glob.h +cmdtab.o: open.h +cmdtab.o: set.h +cmdtab.o: copyright.h +ftp.o: ftp.c +ftp.o: sys.h +ftp.o: util.h +ftp.o: ftp.h +ftp.o: cmds.h +ftp.o: main.h +ftp.o: ftprc.h +ftp.o: getpass.h +ftp.o: defaults.h +ftp.o: copyright.h +ftprc.o: ftprc.c +ftprc.o: sys.h +ftprc.o: util.h +ftprc.o: ftprc.h +ftprc.o: main.h +ftprc.o: cmds.h +ftprc.o: set.h +ftprc.o: defaults.h +ftprc.o: copyright.h +getpass.o: getpass.c +getpass.o: sys.h +getpass.o: util.h +getpass.o: cmds.h +getpass.o: getpass.h +getpass.o: copyright.h +glob.o: glob.c +glob.o: sys.h +glob.o: util.h +glob.o: glob.h +glob.o: cmds.h +glob.o: copyright.h +main.o: main.c +main.o: sys.h +main.o: util.h +main.o: cmds.h +main.o: main.h +main.o: ftp.h +main.o: ftprc.h +main.o: open.h +main.o: set.h +main.o: defaults.h +main.o: copyright.h +open.o: open.c +open.o: sys.h +open.o: util.h +open.o: open.h +open.o: cmds.h +open.o: ftp.h +open.o: ftprc.h +open.o: main.h +open.o: defaults.h +open.o: copyright.h +set.o: set.c +set.o: sys.h +set.o: util.h +set.o: cmds.h +set.o: main.h +set.o: set.h +set.o: defaults.h +set.o: copyright.h +tips.o: tips.c +tips.o: sys.h +tips.o: util.h +util.o: util.c +util.o: sys.h +util.o: util.h +util.o: cmds.h +util.o: main.h +util.o: ftp.h +util.o: ftprc.h +util.o: defaults.h +util.o: copyright.h diff --git a/usr.bin/ncftp/README b/usr.bin/ncftp/README new file mode 100644 index 000000000000..961d56cc5755 --- /dev/null +++ b/usr.bin/ncftp/README @@ -0,0 +1,359 @@ +If you are a novice user, and don't know how to compile things, try +contacting your local guru first (get them to do it for you :-). Please +understand that I don't have time to walk newbies through the whole +installation procedure. + +One of these months, I will write a Configure script that does all this for +you. I just don't have time to learn another language (dist-3.0) just to +write the script! Perhaps for 2.0. + +1. READ this entire file. Part A, below, tells what to do if you want to + use NcFTP with a command-line editor. Part B tells you how to configure + the Makefile to compile the program for your system. Part C tells you + how to configure NcFTP's optional features. Part D tells you how to + contact me if you want to report a bug or submit new code to the + program. + +2. EDIT the Makefile, making any necessary changes described in parts + A, B, or C. Don't forget to read the directions in the Makefile, + so you don't forget any needed libraries, etcetera. + +3. You can also change the program's default behavior by editing defaults.h. + 99% of the time you don't need to do this, so you can skip this step. + +If you have problems, you can mail me, but please try your best to install +it without my help. I'm quite tired of responding to lazy SunOS users +because they didn't bother reading the directions so that they would have +known that they needed to use GCC. + +I _do_ want to hear from you if you have comments or bug reports/fixes. I +would also like to hear from you if you had a system that wasn't covered +in sys.h, so I can add an entry for other users of your system. + +The latest version of ncftp is available in the directory: + cse.unl.edu:/pub/mgleason +This machine is heavily used by students and faculty alike, so please +do not call during working hours (9AM - 5PM American Central time). In fact, +I have a cron entry that changes all the file permissions to public +unreadable during that period, so you won't be able to download anyway. + +I am grateful to Shari Deiana and the University of Nebraska for making this +possible! + + +Part A. Installing with a command line editor: +---------------------------------------------- + +As of this release, GNU Readline and Chris Thewalt's Getline command-line +editing and history facilities are supported. Neither are included with the +ncftp sources. You can find Getline at: + ce.berkeley.edu:/pub/thewalt/getline.tar.Z (note: use 'ls', not 'dir!') +and Readline is in the directory: + prep.ai.mit.edu:/pub/gnu + +To install Readline, you will need to know where libreadline.a and the +header <readline/readline.h> are. You will need to link libreadline.a and +libcurses.a (or libtermcap.a) with ncftp (see the Makefile). Good luck on +trying to compile it. It is not an easy thing to do! In the Makefile, you +will need to add -DREADLINE to PDEFS, add -lreadline -lcurses to LIBS, and +edit the HDRDIRS and LIBDIRS lines. This stuff is already in the Makefile, +so you can just uncomment it. + +To install Getline, you need to know where libgetline.a and it's header +(getline.h) are. In the Makefile, you'll need to add -lgetline to LIBS and +edit the HDRDIRS and LIBDIRS lines. This stuff is already in the Makefile, +so you can just uncomment it. + +DO NOT bug me if you can't figure out how to compile Getline or Readline. +Contact their respective authors instead. It is not essential that you use +them. + + +Part B. System Dependencies: +---------------------------- + +NcFTP may need work-arounds for some things due to the differences +in implementations of unix. The following systems are taken care +of automatically. For these systems, you should just be able to type +'make' (but proceed to part C): + + Silicon Graphics IRIX + IBM's AIX + SINIX + DEC's Ultrix (well, might need to use -lcursesX instead of -lcurses) + NeXT + Pyramid OSx + Berkley Software Design, Inc.'s BSDi + +Otherwise you will have to configure ncftp manually. + +Important for "Yellow Pages" users: Don't forget to link the library +that includes the YP/NIS version of getpwuid(), etc. Otherwise the program +won't be able to expand ~username/path/name type pathnames, and maybe even +~/path/name types of pathnames. If you're wondering why the program isn't +opening your rc file, this could be the cause. + +You will need to add these things to the SDEFS line in the Makefile +as applicable. As an example, if I say 'add -DFoobar to SDEFS,' find +the line in the Makefile that reads 'SDEFS=' (or 'SDEFS=-DFoo2') and +change it to 'SDEFS=-DFoobar' (or 'SDEFS=-DFoo2 -DFoobar). If your +system is listed below, follow the directions and then you ready to +go to part C, below. + + Sun Microsystems' SunOS/Solaris: Use an ANSI compiler such as + gcc (set CC=gcc in the Makefile), or acc (set CC=acc). + The regular 'cc' is not an ANSI compiler. You could also run + something like 'ansi2knr' on the sources and hope it works. + You will probably need to link both the curses and termcap + libraries if you use -DCURSES (see below). If you're running + Solaris (SunOS 5.x or greater) add -DSolaris to SDEFS. + I also needed to add -lnsl -lsocket to LIBS. + + Hewlett-Packard HP-UX: If you have 7.0, you'll need to find + a copy of <ftp.h> from somewhere (8.0 has it though). Then + set CFLAGS= -Aa. You may also need to use gcc if your + compiler is non-ANSI. Note that for HP-UX, the default + terminal escape codes are for HP terminals, so you should + probably link termcap/curses in so it will get the ANSI + sequences if you're on a vt100, etc., terminal connected + to your HP-UX machine. + + Linux: For 'term' support, from what I can tell just add + the path of 'client.a' to LIBS, and add -DTERM_FTP to SDEFS, + to turn on the term specific ftp code. May need to link + -lcurses and -ltermcap. + + SCO Unix: Add -DSCO324 or -DSCO322 (as appropriate) to SDEFS, + and -lsocket to LIBS. + + SCO Xenix 2.3.4: Add -DSCOXNX to SDEFS; + Try adding -DLINGER if puts don't work. + Add "-lsocket -ldir" to LIBS. + + Bull DPX/2: Add -DBULL to SDEFS, add -linet to LIBS, and + use gcc. + + Sequent's DYNIX: Use gcc and add -DDYNIX (if necessary) to SDEFS. + You may also be short several string functions which you will + have to get elsewhere, and perhaps mktime and strftime. + You can get all that stuff from the BSD sources (like ftp.uu.net). + Please bug Sequent to update their libc library! + + Sequent's Dynix/PTX: Add -DDYNIXPTX to SDEFS. + Add -lsocket -linet -lnsl -lseq to LIBS. + + DEC OSF1/1.3: Use gcc, Add -DGETCWDSIZET to SDEFS. cc might work, + though. Try cc if gcc chokes. + +If your system doesn't fit any of those, things will be trickier. Answer +all these questions and add to the SDEFS line. You may want to try +each option one at a time until everything works. + +* Is your system closer to System V or BSD? Your SDEFS line should have +either -DBSD or -DSYSV. If you don't know, try leaving it blank first; +some compilers automatically define it for you. + +* Add -DNO_CONST if your compiler chokes on the const directive. You +will know if you need to add this if the compiler spits out errors saying +it doesn't know what 'const' is. + +* As I said above, you will need to link special libraries if your system +is running Yellow Pages. + +* Add -DSYSSELECTH if you need <sys/select.h> included for definitions +of fd_set, etc. + +* Add -DNO_UNISTDH if you don't have <unistd.h>. If the compiler complains +about not being able to open <unistd.h> add this. + +* Add -DNO_STDLIBH if you don't have <stdlib.h>. If the compiler complains +about not being able to open <stdlib.h> add this. + +* Add -DNO_UTIMEH if you don't have <utime.h>. If the compiler complains +about not being able to open <utime.h> add this. + +* Add -DNO_MKTIME if you don't have the mktime() system call, and don't +feel like getting the source for it and compiling it in with the program. +If you define this, the program will not set the file modification times +to match the ones on the remote host (no big deal). + +* Add -DGETPASS if you would rather use the standard getpass() system +call, instead of our version, Getpass(), which takes more than 8 +characters. You may want to define this if you are having problems +compiling getpass.c. + +If you haven't given up on our Getpass(), you can try adding -DSGTTYB +if you want to use a struct sgttyb instead of a struct termio. By default, +BSD systems define SGTTYB automatically. You can also try adding -DTERMIOS +to use a POSIX compliant struct termios instead. Don't pull your hair out +trying to get the Getpass code to compile; if it gives you problems just +define -DGETPASS and hope your system's getpass can handle passwords +longer than 8 characters. + +* Add -DBAD_INETADDR if your inet_addr() function returns a struct in_addr +instead of a u_long, as it should (in DG/UX 5.4.1). + +* Add -DBROKEN_MEMCPY if ncftp mysteriously dumps core when trying to open +a remote host. I'm told that this happens because of some problem in System +V's sockets don't like fprintf (and memcpy). + +* Add -DPTRTYPE=char if your pre-ANSI compiler complains about the +way malloc() or free() are used, and in general does not like (void *) +as a generic pointer type. + +* Add -DNO_STRFTIME if your system does not have strftime(). If you do, +we won't try to use it. This means, however, you cannot use ``%'' values +in your prompt. + +* Add -DNO_RENAME if your system does not have rename() (or the one it +has is broken). If you do, we will use our own. + +* Add -DNO_STRSTR if your system does not have strstr(). If you do, we +will use our own. + +* Add -DLINGER if puts to the remote system are incomplete. + +* Add -DNET_ERRNO_H if you need to include <net/errno.h> for definitions + of ECONNREFUSED, etcetera. + +* (Optional) Add -DGETCWDSIZET if your system's getcwd() takes a size_t +as the second parameter instead of an int. + +* (Optional) Add -DHERROR if you know you have the herror() system +call. + +* (Optional) Add -DU_WAIT if you know your wait system call takes +a pointer to a 'union wait.' Defined automatically if you define +BSD. + +* (Optional) Add -DHOSTNAME=\"machine.domain.nam\" if your system +doesn't generate it's own hostname. To check this, compile ncftp +then run it and type 'set.' Look at the variable anon-password. +If the hostname is wrong, or if it is in the form of 'yourlogin' or +'yourlogin@machine' instead of 'yourlogin@machine.xxx.yyy,' +re-compile it with HOSTNAME set to your machine's address, in the +form of 'machine.xxx.yyy.' + +* (Optional) Add -DHAS_DOMAINNAME if you have the getdomainname() + function. + +* (Optional) If you're having problems with your hostname not being +full (i.e you have 'yourlogin@machine') all is not lost. First of all, +define HAS_DOMAINNAME if you can. But sometimes getdomainname() doesn't +work -- instead of giving you the domain name, it returns an empty +string. So you can hardcode the domain name by defining DOMAIN_NAME to +be the domain (i.e. add -DDOMAIN_NAME=\"domain.nam\"). That way, if +getdomainname doesn't work, the program will have something to fall back +on. This problem is common on SunOS/Solaris. + +* (Optional) Add -DSTRICT_PROTOS if your compiler wants function prototypes +for all functions, not just non-int-returning ones. This is really just +handy for debugging during development, so this is not recommended. + + +Part C. Program Options: +------------------------ + +Add these as applicable to the PDEFS line in the Makefile. + +* -DGZCAT=\"path\": If you have the GNU gzip package installed on your system, + the program can try paging remote files compressed with gzip _and_ + compress (instead of just compress). Add -DGZCAT=\"/full/path/to/zcat\" + with GZCAT set to the path name of GNU's zcat/gzcat. + +* -DCURSES: Uses curses library to display boldface, underline, etc. + By default ncftp uses hard-coded ANSI escapes (^[[1m etc.) to + save the 100k or so the curses library adds. You will also need + to edit the LIBS line in the Makefile to add -lcurses. You may + need to add -ltermcap instead, or both -lcurses and -ltermcap. + If you choose to use the termcap library, you may want to also add + -DNO_CURSES_H so it does not try to include <curses.h>. + +* -DSYSLOG: Define this to have ncftp log connections and transfers + to the syslog. + +* -DNO_TIPS: Define if you want to cut a little fat at the expense of + novice users. + +* -DGETLINE: If you want to use Chris Thewalt's getline input line editor + and history facility, add this (and see below). + +* -DREADLINE: If you want to use GNU's readline input line editor and + history facility, add this (and see the Makefile). If you do this, you + also need to add -DCURSES (see above). + +* -DSOCKS: NcFTP is now compatible with the Socks library by David Koblas, + at koblas@sgi.com. This lets you use NcFTP with a "firewall" gateway + for enhanced site security. You can get the latest version from + netcom.com:/pub/koblas. After you have compiled it, compile NcFTP + with -DSOCKS added to PDEFS, and the pathname of the Rconnect.o file + added to LIBS. + +* -DTRY_ABOR: Define if you want to try the 'ABOR' command from ncftp; + The aborting code has had some problems, so by default the program + 'aborts' by continuing to read input but not echoing output. + +* -DDB_ERRS: Define this if you want my Perror() function to be more + verbose. You may want to do this if you are a programmer examining this + code, and want to know where in the source the Perror's are coming + from. + +Part D. Sending me patches: +--------------------------- + +I apologize in advance for problems that my coding style may cause. The code +itself is formatted such that each indent-level is a tab (intended to be +equivalent to 4 spaces), and not spaces nor a combination of tabs and spaces. +The reason for this, besides being more logical to me, is that I use a +Macintosh editor to compose the code and I prefer it's indenting method. +Another problem in my coding-style is that I write C-code intended for ANSI +C compilers. This means that I will use the new-style function declarations +and function prototypes, like: + + + long Foobar(long, long, char *); + long Foobar(long t0, long t1, char *str) + { + } + +as opposed to: + + long Foobar(); + long Foobar(t0, t1, str) + long t0, t1; + char *str; + { + } + +Another thing may annoy you is that I always use function prototypes for any +function I call, including functions that return an int. This is a good +practice that I learned from the Macintosh programming world. + +So if you send me patches, please conform to my coding style so that 'patch' +won't screw up, and also that some continuity will be preserved. + +Before you make your patch, you should be sure that you are using the most +current version of the program. This is especially important if you are +reporting a bug; I may have already fixed it! See the above info to get it +via ftp. Major versions are always posted to comp.sources.misc. Bug reports +are posted to comp.sources.bugs. Patches that need to be posted ASAP are +posted to this group first, so it is advisable that you check this group. + +If you make changes to the code, surround your code in #ifdef/#endif blocks. +Instead of doing things like #ifdef SunOS, use a name that describes the +bug fix or feature, and not your system type, like #ifdef NO_UNISTDH, +or #ifdef GETLINE. That way in case another system has the same problem, +it can be added to sys.h without cluttering up the source code. Then, add +the symbol you used to the end of cmds.c, in the 'CPP Hell' part of the +version() command. You'll see a list of CPP symbols, so just add yours in +a similar fashion, like #ifdef GETLINE/DStrs[nDStrs++] = "GETLINE";/#endif. + +If you don't know how to make a patch, here's how to do it. Things are easy +if you've only changed one file. Then all you need to do pipe the output of +diff -c into a file and send it to me, i.e. "diff -c cmds.c cmds.c.hack >pch." +If you've hacked several files, the way I do it is to keep the originals in +one directory (you did make a copy of everything first didn't you?) and the +revisions in another directory. Then you change directory to the one with the +originals and do "diff -c . ../revisions > ../pch." + +--mg (mgleason@cse.unl.edu) diff --git a/usr.bin/ncftp/cmds.c b/usr.bin/ncftp/cmds.c new file mode 100644 index 000000000000..41394dbb2c4e --- /dev/null +++ b/usr.bin/ncftp/cmds.c @@ -0,0 +1,2214 @@ +/* cmds.c */ + +/* $RCSfile: cmds.c,v $ + * $Revision: 14020.14 $ + * $Date: 93/07/09 11:31:53 $ + */ + +#include "sys.h" + +#include <sys/wait.h> + +#include <sys/stat.h> +#include <arpa/ftp.h> +#include <setjmp.h> +#include <signal.h> +#include <errno.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <ctype.h> + +#ifdef SYSLOG +# include <syslog.h> +#endif + +#include "util.h" +#include "cmds.h" +#include "main.h" +#include "ftp.h" +#include "ftprc.h" +#include "getpass.h" +#include "glob.h" +#include "open.h" +#include "set.h" +#include "defaults.h" +#include "copyright.h" + +/* cmds.c globals */ +#ifdef PASSIVEMODE +int passivemode = 1; +#endif +int curtype; /* file transfer type */ +char *typeabbrs = "abiet"; +str32 curtypename; /* name of file transfer type */ +int verbose; /* verbosity level of output */ +int mprompt; /* interactively prompt on m* cmds */ +int debug; /* debugging level */ +int options; /* used during socket creation */ +int macnum; /* number of defined macros */ +int paging = 0; +int creating = 0; +struct macel macros[MAXMACROS]; +char *macbuf; /* holds ALL macros */ +int doingInitMacro = 0; /* TRUE if executing "init" macro. */ +jmp_buf jabort; +char *mname; /* name of current m* command */ +int activemcmd; /* flag: if != 0, then active multi command */ +int warnNoLSFlagsWithWildcards = 0; + /* Tells whether the user has been + * warned about not being able to use + * flags with ls when using wildcards. + */ +longstring cwd; /* current remote directory */ +longstring lcwd; /* current local directory */ +Hostname lasthostname; /* name of last host w/ lookup(). */ +int logged_in = 0; /* TRUE if connected and user/pw OK. */ +int is_ls = 0; /* are we doing an ls? if so, then + read input into a line buffer + for re-use. */ +extern int buffer_only; +struct lslist *lshead = NULL; /* hold last output from host */ +struct lslist *lstail = NULL; + +/* cmds.c externs */ +extern char *globerr, *home, *reply_string; +extern int margc, connected, ansi_escapes; +extern int code, connected; +extern int toatty, fromatty; +extern int data, progress_meter, remote_is_unix; +extern int parsing_rc, keep_recent; +extern char *altarg, *line, *margv[]; +extern char *globchars; +extern Hostname hostname; +extern RemoteSiteInfo gRmtInfo; +extern string progname, pager, anon_password; +extern string prompt, version, indataline; +extern longstring logfname; +extern long logsize; +extern size_t xferbufsize; +extern struct servent serv; +extern struct cmd cmdtab[]; +extern struct userinfo uinfo; +extern FILE *cin, *cout, *logf; +extern int Optind; +extern char *Optarg; +extern int Optind; +extern char *Optarg; + +#ifdef STRICT_PROTOS +extern int gethostname(char *, int), getdomainname(char *, int); +#endif + + +struct types types[] = { + { "ascii", "A", TYPE_A, 0 }, + { "binary", "I", TYPE_I, 0 }, + { "image", "I", TYPE_I, 0 }, + { "ebcdic", "E", TYPE_E, 0 }, + { "tenex", "L", TYPE_L, "8" }, + { 0 } +}; + + + +long GetDateSizeFromLSLine(char *fName, unsigned long *mod_time) +{ + char *cp, *np; + string lsline; + long size = SIZE_UNKNOWN; + int n, v; + struct lslist *savedh, *savedt; + static int depth = 0; + + depth++; /* Try to prevent infinite recursion. */ + *mod_time = MDTM_UNKNOWN; + v = verbose; verbose = V_QUIET; + is_ls = 1; + buffer_only = 1; + savedh = lshead; + savedt = lstail; + lshead = NULL; + (void) recvrequest("LIST", "-", fName, "w"); + is_ls = 0; + buffer_only = 0; + verbose = v; + if (lshead == NULL) { + PurgeLineBuffer(); + lshead = savedh; + lstail = savedt; + goto aa; + } + (void) Strncpy(lsline, lshead->string); + PurgeLineBuffer(); + lshead = savedh; + lstail = savedt; + + if (code >= 400 && code < 500) + goto aa; + + /* See if this line looks like a unix-style ls line. + * If so, we can grab the date and size from it. + */ + if (strpbrk(lsline, "-dlsbcp") == lsline) { + /* See if it looks like a typical '-rwxrwxrwx' line. */ + cp = lsline + 1; + if (*cp != 'r' && *cp != '-') + goto aa; + ++cp; + if (*cp != 'w' && *cp != '-') + goto aa; + cp += 2; + if (*cp != 'r' && *cp != '-') + goto aa; + + /* skip mode, links, owner (and possibly group) */ + for (n = 0; n < 4; n++) { + np = cp; + while (*cp != '\0' && !isspace(*cp)) + cp++; + while (*cp != '\0' && isspace(*cp)) + cp++; + } + if (!isdigit(*cp)) + cp = np; /* back up (no group) */ + (void) sscanf(cp, "%ld%n", &size, &n); + + *mod_time = UnLSDate(cp + n + 1); + + if (size < 100) { + /* May be the size of a link to the file, instead of the file. */ + if ((cp = strstr(lsline, " -> ")) != NULL) { + /* Yes, it was a link. */ + size = (depth>4) ? SIZE_UNKNOWN : + GetDateAndSize(cp + 4, mod_time); + /* Try the file. */ + } + } + } +aa: + --depth; + return (size); +} /* GetDateSizeFromLSLine */ + + + + +/* The caller wanted to know the modification date and size of the remote + * file given to us. We try to get this information by using the SIZE + * and MDTM ftp commands, and if that didn't work we try sending the site + * a "ls -l <fName>" and try to get that information from the line it + * sends us back. It is possible that we won't be able to determine + * either of these, though. + */ +long GetDateAndSize(char *fName, unsigned long *mod_time) +{ + unsigned long mdtm, ls_mdtm; + long size, ls_size; + int have_mdtm, have_size; + string cmd; + + size = SIZE_UNKNOWN; + mdtm = MDTM_UNKNOWN; + if (fName != NULL) { + have_mdtm = have_size = 0; + if (gRmtInfo.hasSIZE) { + (void) Strncpy(cmd, "SIZE "); + (void) Strncat(cmd, fName); + if (quiet_command(cmd) == 2) { + if (sscanf(reply_string, "%*d %ld", &size) == 1) + have_size = 1; + } else if (strncmp(reply_string, "550", (size_t)3) != 0) + gRmtInfo.hasSIZE = 0; + } + +#ifndef NO_MKTIME + /* We'll need mktime() to un-mangle this. */ + if (gRmtInfo.hasMDTM) { + (void) Strncpy(cmd, "MDTM "); + (void) Strncat(cmd, fName); + if (quiet_command(cmd) == 2) { + /* Result should look like "213 19930602204445\n" */ + mdtm = UnMDTMDate(reply_string); + if (mdtm != MDTM_UNKNOWN) + have_mdtm = 1; + } else if (strncmp(reply_string, "550", (size_t)3) != 0) + gRmtInfo.hasMDTM = 0; + } +#endif /* NO_MKTIME */ + + if (!have_mdtm || !have_size) + ls_size = GetDateSizeFromLSLine(fName, &ls_mdtm); + + /* Try to use the information from the real SIZE/MDTM commands if + * we could, since some maverick ftp server may be using a non-standard + * ls command, and we could parse it wrong. + */ + + if (!have_mdtm) + mdtm = ls_mdtm; + if (!have_size) + size = ls_size; + + dbprintf("Used SIZE: %s; Used MDTM: %s\n", + have_size ? "yes" : "no", + have_mdtm ? "yes" : "no" + ); + + if (debug > 0) { + if (size != SIZE_UNKNOWN) + dbprintf("Size: %ld\n", size); + if (mdtm != MDTM_UNKNOWN) + dbprintf("Mdtm: %s\n", ctime((time_t *) &mdtm)); + } + } + *mod_time = mdtm; + return size; +} /* GetDateAndSize */ + + + + + +int _settype(char *typename) +{ + register struct types *p; + int comret, c; + string cmd; + char *cp; + + c = isupper(*typename) ? tolower(*typename) : (*typename); + if ((cp = index(typeabbrs, c)) != NULL) + p = &types[(int) (cp - typeabbrs)]; + else { + (void) printf("%s: unknown type\n", typename); + return USAGE; + } + if (c == 't') + (void) strcpy(cmd, "TYPE L 8"); + else + (void) sprintf(cmd, "TYPE %s", p->t_mode); + comret = command(cmd); + if (comret == COMPLETE) { + (void) Strncpy(curtypename, p->t_name); + curtype = p->t_type; + } + return NOERR; +} /* _settype */ + + + + +int SetTypeByNumber(int i) +{ + char tstr[4], *tp = tstr, c; + + tp[1] = c = 0; + switch (i) { + case TYPE_A: c = 'a'; break; + case TYPE_I: c = 'b'; break; + case TYPE_E: c = 'e'; break; + case TYPE_L: c = 't'; + } + *tp = c; + return (c == 0 ? -1 : _settype(tp)); +} /* SetTypeByNumber */ + + + + +/* + * Set transfer type. + */ +int settype(int argc, char **argv) +{ + int result = NOERR; + + if (argc > 2) { + result = USAGE; + } else { + if (argc < 2) + goto xx; + result = _settype(argv[1]); + if (IS_VVERBOSE) +xx: (void) printf("Using %s mode to transfer files.\n", curtypename); + } + return result; +} /* settype */ + + + + +/*ARGSUSED*/ +int setbinary(int argc, char **argv) { return (_settype("binary")); } +/*ARGSUSED*/ +int setascii(int argc, char **argv) { return (_settype("ascii")); } + + + +/* + * Send a single file. + */ +int put(int argc, char **argv) +{ + char *cmd; + + if (argc == 2) { + argc++; + argv[2] = argv[1]; + } + if (argc < 2) + argv = re_makeargv("(local-file) ", &argc); + if (argc < 2) { +usage: + return USAGE; + } + if (argc < 3) + argv = re_makeargv("(remote-file) ", &argc); + if (argc < 3) + goto usage; + cmd = (argv[0][0] == 'a') ? "APPE" : "STOR"; + (void) sendrequest(cmd, argv[1], argv[2]); + return NOERR; +} /* put */ + + + + +/* + * Send multiple files. + */ +int mput(int argc, char **argv) +{ + register int i; + Sig_t oldintr; + char *tp; + + if (argc < 2) + argv = re_makeargv("(local-files) ", &argc); + if (argc < 2) { + return USAGE; + } + mname = argv[0]; + activemcmd = 1; + oldintr = Signal(SIGINT, mabort); + (void) setjmp(jabort); + for (i = 1; i < argc; i++) { + register char **cpp, **gargs; + char *icopy; + + /* Make a copy of the argument, because glob() will just copy + * the pointer you give it to the glob-arg vector, and blkfree() + * will want to free each element of the glob-arg vector + * later. + */ + if ((icopy = NewString(argv[i])) == NULL) + break; + gargs = glob(icopy); + if (globerr != NULL) { + (void) printf("%s\n", globerr); + if (gargs) { + blkfree(gargs); + Free(gargs); + } + continue; + } + for (cpp = gargs; cpp && *cpp != NULL; cpp++) { + if (activemcmd && confirm(argv[0], *cpp)) { + tp = *cpp; + (void) sendrequest("STOR", *cpp, tp); + if (!activemcmd && fromatty) { + if (confirm("Continue with","mput")) { + activemcmd++; + } + } + } + } + if (gargs != NULL) { + blkfree(gargs); + Free(gargs); + } + } + (void) Signal(SIGINT, oldintr); + activemcmd = 0; + return NOERR; +} /* mput */ + + + + +int rem_glob_one(char *pattern) +{ + int oldverbose, result = 0; + char *cp; + string str, tname; + FILE *ftemp; + + /* Check for wildcard characters. */ + if (*pattern == '|' || strpbrk(pattern, globchars) == NULL) + return 0; + + (void) tmp_name(tname); + oldverbose = verbose; + verbose = V_QUIET; + (void) recvrequest ("NLST", tname, pattern, "w"); + verbose = oldverbose; + ftemp = fopen(tname, "r"); + (void) chmod(tname, 0600); + if (ftemp == NULL || FGets(str, ftemp) == NULL) { + if (NOT_VQUIET) + (void) printf("%s: no match.\n", pattern); + result = -1; + goto done; + } + if ((cp = index(str, '\n')) != NULL) + *cp = '\0'; + (void) strcpy(pattern, str); + cp = FGets(str, ftemp); + /* It is an error if the pattern matched more than one file. */ + if (cp != NULL) { + if (NOT_VQUIET) + (void) printf("?Ambiguous remote file name.\n"); + result = -2; + } +done: + if (ftemp != NULL) + (void) fclose(ftemp); + (void) unlink(tname); + return (result); +} /* rem_glob_one */ + + + + +/* + * Receive (and maybe page) one file. + */ +int get(int argc, char **argv) +{ + string local_file; + char remote_file[256]; + char *cp; + int oldtype = curtype, try_zcat; + size_t len; + + /* paging mode is set if the command name is 'page' or 'more.' */ + paging = (**argv != 'g'); + + if (argc < 2) + argv = re_makeargv("(remote-file) ", &argc); + + if (argc < 2) { + return USAGE; + } + cp = Strncpy(remote_file, argv[1]); + argv[1] = cp; + if (rem_glob_one(argv[1]) < 0) + return CMDERR; + + if (paging) { + try_zcat = 0; + len = strlen(remote_file); + + if (len > (size_t) 2) { + if (remote_file[len-2] == '.') { + /* Check for .Z files. */ + if (remote_file[len-1] == 'Z') + try_zcat = 1; +#ifdef GZCAT + /* Check for .z (gzip) files. */ + if (remote_file[len-1] == 'z') + try_zcat = 1; +#endif /* GZCAT */ + } + } + +#ifdef GZCAT + if (len > (size_t) 3) { + /* Check for ".gz" (gzip) files. */ + if (strcmp(remote_file + len - 3, ".gz") == 0) + try_zcat = 1; + } +#endif /* GZCAT */ + + /* Run compressed remote files through zcat, then the pager. + * If GZCAT was defined, we also try paging gzipped files. + * Note that ZCAT is defined to be GZCAT if you defined + * GZCAT. + */ + + if (try_zcat) { + (void) _settype("b"); + (void) sprintf(local_file, "|%s ", ZCAT); + argv[2] = Strncat(local_file, pager); + } else { + /* Try to use text mode for paging, so newlines get converted. */ + (void) _settype("a"); + argv[2] = pager; + } + } else { + /* normal get */ + if (argc == 2) { + (void) Strncpy(local_file, argv[1]); + argv[2] = local_file; + } else { + if (argc < 3) + argv = re_makeargv("(local-file) ", &argc); + if (argc < 3) + return USAGE; + (void) LocalDotPath(argv[2]); + } + } + (void) recvrequest("RETR", argv[2], argv[1], "w"); + if (paging) { + (void) SetTypeByNumber(oldtype); /* Restore it to what it was. */ + paging = 0; + } + return NOERR; +} /* get */ + + + +/*ARGSUSED*/ +void mabort SIG_PARAMS +{ + (void) printf("\n"); + (void) fflush(stdout); + if (activemcmd && fromatty) { + if (confirm("Continue with", mname)) { + longjmp(jabort,0); + } + } + activemcmd = 0; + longjmp(jabort,0); +} /* mabort */ + + + + +/* + * Get multiple files. + */ +int mget(int argc, char **argv) +{ + char *cp; + longstring local; + Sig_t oldintr; + + if (argc < 2) + argv = re_makeargv("(remote-files) ", &argc); + if (argc < 2) { + return USAGE; + } + mname = argv[0]; + activemcmd = 1; + oldintr = Signal(SIGINT, mabort); + (void) setjmp(jabort); + while ((cp = remglob(argv)) != NULL) { + if (*cp == '\0') { + activemcmd = 0; + continue; + } + if (activemcmd && confirm(argv[0], cp)) { + (void) Strncpy(local, cp); + (void) recvrequest("RETR", local, cp, "w"); + if (!activemcmd && fromatty) { + if (confirm("Continue with","mget")) { + activemcmd++; + } + } + } + } + (void) Signal(SIGINT,oldintr); + activemcmd = 0; + return NOERR; +} /* mget */ + + + + +char *remglob(char *argv[]) +{ + static FILE *ftemp = NULL; + int oldverbose, i; + char *cp, *mode; + static string tmpname, str; + int result, errs; + + if (!activemcmd) { +xx: + if (ftemp) { + (void) fclose(ftemp); + ftemp = NULL; + (void) unlink(tmpname); + } + return(NULL); + } + if (ftemp == NULL) { + (void) tmp_name(tmpname); + oldverbose = verbose, verbose = V_QUIET; + errs = 0; + for (mode = "w", i=1; argv[i] != NULL; i++, mode = "a") { + result = recvrequest ("NLST", tmpname, argv[i], mode); + if (i == 1) + (void) chmod(tmpname, 0600); + if (result < 0) { + fprintf(stderr, "%s: %s.\n", + argv[i], + (strpbrk(argv[i], globchars) != NULL) ? "No match" : + "No such file" + ); + errs++; + } + } + verbose = oldverbose; + if (errs == (i - 1)) { + /* Every pattern was in error, so we can't try anything. */ + (void) unlink(tmpname); /* Shouldn't be there anyway. */ + return NULL; + } + ftemp = fopen(tmpname, "r"); + if (ftemp == NULL) { + PERROR("remglob", tmpname); + return (NULL); + } + } + if (FGets(str, ftemp) == NULL) + goto xx; + if ((cp = index(str, '\n')) != NULL) + *cp = '\0'; + return (str); +} /* remglob */ + + +/* + * Turn on/off printing of server echo's, messages, and statistics. + */ +int setverbose(int argc, char **argv) +{ + if (argc > 1) + set_verbose(argv[1], 0); + else set_verbose(argv[1], -1); + return NOERR; +} /* setverbose */ + + + +/* + * Toggle interactive prompting + * during mget, mput, and mdelete. + */ +int setprompt(int argc, char **argv) +{ + if (argc > 1) + mprompt = StrToBool(argv[1]); + else mprompt = !mprompt; + if (IS_VVERBOSE) + (void) printf("Interactive prompting for m* commmands %s.\n", onoff(mprompt)); + return NOERR; +} /* setprompt */ + + + + +void fix_options(void) +{ + if (debug) + options |= SO_DEBUG; + else + options &= ~SO_DEBUG; +} /* fix_options */ + + +/* + * Set debugging mode on/off and/or + * set level of debugging. + */ +int setdebug(int argc, char **argv) +{ + int val; + + if (argc > 1) { + val = StrToBool(argv[1]); + if (val < 0) { + (void) printf("%s: bad debugging value.\n", argv[1]); + return USAGE; + } + } else + val = !debug; + debug = val; + fix_options(); + if (IS_VVERBOSE) + (void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug); + return NOERR; +} /* debug */ + + + +/* + * Set current working directory + * on remote machine. + */ +int cd(int argc, char **argv) +{ + if (argc < 2) + argv = re_makeargv("(remote-directory) ", &argc); + if (argc < 2) { + return USAGE; + } + (void) _cd(argv[1]); + return NOERR; +} /* cd */ + + + + +int implicit_cd(char *dir) +{ + int i, j = 0; + + if (connected) { + i = verbose; + /* Special verbosity level that ignores errors and prints other stuff, + * so you will get just the unknown command message and not an error + * message from cd. + */ + verbose = V_IMPLICITCD; + j = _cd(dir); + verbose = i; + } + return j; +} /* implicit_cd */ + + + + +int _cd(char *dir) +{ + register char *cp; + int result = 0; + string str; + + if (dir == NULL) + goto getrwd; + /* Won't work because glob really is a ls, so 'cd pu*' will match + * pub/README, pub/file2, etc. + * if (result = rem_glob_one(dir) < 0) + * return result; + */ + if (strncmp(dir, "CDUP", (size_t) 4) == 0) + (void) Strncpy(str, dir); + else + (void) sprintf(str, "CWD %s", dir); + if (command(str) != 5) { +getrwd: + (void) quiet_command("PWD"); + cp = rindex(reply_string, '\"'); + if (cp != NULL) { + result = 1; + *cp = '\0'; + cp = index(reply_string, '\"'); + if (cp != NULL) + (void) Strncpy(cwd, ++cp); + } + } + dbprintf("Current remote directory is \"%s\"\n", cwd); + return (result); +} /* _cd */ + + + + +/* + * Set current working directory + * on local machine. + */ +int lcd(int argc, char **argv) +{ + longstring ldir; + + if (argc < 2) + argc++, argv[1] = home; + if (argc != 2) { + return USAGE; + } + (void) Strncpy(ldir, argv[1]); + if (chdir(LocalDotPath(ldir)) < 0) { + PERROR("lcd", ldir); + return CMDERR; + } + (void) get_cwd(lcwd, (int) sizeof(lcwd)); + if (NOT_VQUIET) + (void) printf("Local directory now %s\n", lcwd); + return NOERR; +} /* lcd */ + + + + +/* + * Delete a single file. + */ +int do_delete(int argc, char **argv) +{ + string str; + + if (argc < 2) + argv = re_makeargv("(remote file to delete) ", &argc); + if (argc < 2) { + return USAGE; + } + if (rem_glob_one(argv[1]) == 0) { + (void) sprintf(str, "DELE %s", argv[1]); + (void) command(str); + } + return NOERR; +} /* do_delete */ + + + + +/* + * Delete multiple files. + */ +int mdelete(int argc, char **argv) +{ + char *cp; + Sig_t oldintr; + string str; + + if (argc < 2) + argv = re_makeargv("(remote-files) ", &argc); + if (argc < 2) { + return USAGE; + } + mname = argv[0]; + activemcmd = 1; + oldintr = Signal(SIGINT, mabort); + (void) setjmp(jabort); + while ((cp = remglob(argv)) != NULL) { + if (*cp == '\0') { + activemcmd = 0; + continue; + } + if (activemcmd && confirm(argv[0], cp)) { + (void) sprintf(str, "DELE %s", cp); + (void) command(str); + if (!activemcmd && fromatty) { + if (confirm("Continue with", "mdelete")) { + activemcmd++; + } + } + } + } + (void) Signal(SIGINT, oldintr); + activemcmd = 0; + return NOERR; +} /* mdelete */ + + + + +/* + * Rename a remote file. + */ +int renamefile(int argc, char **argv) +{ + string str; + + if (argc < 2) + argv = re_makeargv("(from-name) ", &argc); + if (argc < 2) { +usage: + return USAGE; + } + if (argc < 3) + argv = re_makeargv("(to-name) ", &argc); + if (argc < 3) + goto usage; + if (rem_glob_one(argv[1]) < 0) + return CMDERR; + (void) sprintf(str, "RNFR %s", argv[1]); + if (command(str) == CONTINUE) { + (void) sprintf(str, "RNTO %s", argv[2]); + (void) command(str); + } + return NOERR; +} /* renamefile */ + + + +/* + * Get a directory listing + * of remote files. + */ +int ls(int argc, char **argv) +{ + char *whichcmd, *cp; + str32 lsflags; + string remote, local, str; + int listmode, pagemode, i; + + PurgeLineBuffer(); + pagemode = 0; + switch (**argv) { + case 'p': /* pls, pdir, pnlist */ + pagemode = 1; + listmode = argv[0][1] == 'd'; + break; + case 'd': /* dir */ + listmode = 1; + break; + default: /* ls, nlist */ + listmode = 0; + } + whichcmd = listmode ? "LIST" : "NLST"; + + (void) strncpy(local, (pagemode ? pager : "-"), sizeof(local)); + remote[0] = lsflags[0] = 0; + + /* Possible scenarios: + * 1. ls + * 2. ls -flags + * 3. ls directory + * 4. ls -flags >outfile + * 5. ls directory >outfile + * 6. ls -flags directory + * 7. ls -flags directory >outfile + * + * Note that using a wildcard will choke with flags. I.e., don't do + * "ls -CF *.tar," but instead do "ls *.tar." + */ + + for (i=1; i<argc; i++) { + switch (argv[i][0]) { + case '-': + /* + * If you give more than one set of flags, concat the each + * additional set to the first one (without the dash). + */ + (void) strncat(lsflags, (argv[i] + (lsflags[0] == '-')), sizeof(lsflags)); + break; + case '|': + (void) Strncpy(local, argv[i]); + LocalDotPath(local + 1); + break; + case '>': + /* We don't want the '>'. */ + (void) Strncpy(local, argv[i] + 1); + LocalDotPath(local); + break; + default: + cp = argv[i]; + /* + * In case you want to get a remote file called '--README--' + * or '>README,' you can use '\--README--' and '\>README.' + */ + if ((cp[1] != 0) && (*cp == '\\')) + ++cp; + if (remote[0] != 0) { + (void) Strncat(remote, " "); + (void) Strncat(remote, cp); + } else { + (void) Strncpy(remote, cp); + } + } /* end switch */ + } /* end loop */ + + /* + * If we are given an ls with some flags, make sure we use + * columnized output (-C) unless one column output (-1) is + * specified. + */ + if (!listmode) { + if (lsflags[0] != 0) { + (void) Strncpy(str, lsflags); + for (cp = str + 1; *cp; cp++) + if (*cp == '1') + goto aa; + (void) sprintf(lsflags, "-FC%s", str + 1); + } else { + if (remote_is_unix) + (void) strcpy(lsflags, "-FC"); + } + /* As noted above, we can't use -flags if the user gave a + * wildcard expr. + */ + if (remote_is_unix && (strpbrk(remote, globchars) != NULL)) { + lsflags[0] = 0; + /* Warn the user what's going on. */ + if ((warnNoLSFlagsWithWildcards == 0) && NOT_VQUIET) { + (void) fprintf(stderr, "Warning: ls flags disabled with wildcard expressions.\n"); + warnNoLSFlagsWithWildcards++; + } + } + } + +aa: + is_ls = 1; /* tells getreply() to start saving input to a buffer. */ + (void) Strncpy(str, remote); + if (lsflags[0] && remote[0]) + (void) sprintf(remote, "%s%c%s", lsflags, LS_FLAGS_AND_FILE, str); + else + (void) strncpy(remote, lsflags[0] ? lsflags : str, sizeof(remote)); + (void) recvrequest(whichcmd, local, (remote[0] == 0 ? NULL : remote), "w"); + is_ls=0; + return NOERR; +} /* ls */ + + + +/* + * Do a shell escape + */ +/*ARGSUSED*/ +int shell(int argc, char **argv) +{ + int pid; + Sig_t old1, old2; + char *theShell, *namep; +#ifndef U_WAIT + int Status; +#else + union wait Status; +#endif + string str; + + old1 = signal (SIGINT, SIG_IGN); + old2 = signal (SIGQUIT, SIG_IGN); + /* This will prevent <defunct> zombie processes. */ + /* (void) signal(SIGCHLD, SIG_IGN); */ + + if ((pid = fork()) == 0) { + for (pid = 3; pid < 20; pid++) + (void) close(pid); + (void) Signal(SIGINT, SIG_DFL); + (void) Signal(SIGQUIT, SIG_DFL); + if ((theShell = getenv("SHELL")) == NULL) + theShell = uinfo.shell; + if (theShell == NULL) + theShell = "/bin/sh"; + namep = rindex(theShell, '/'); + if (namep == NULL) + namep = theShell; + (void) strcpy(str, "-"); + (void) strcat(str, ++namep); + if (strcmp(namep, "sh") != 0) + str[0] = '+'; + dbprintf ("%s\n", theShell); +#if defined(BSD) || defined(_POSIX_SOURCE) + setreuid(-1,getuid()); + setregid(-1,getgid()); +#endif + if (argc > 1) + (void) execl(theShell, str, "-c", altarg, (char *)0); + else + (void) execl(theShell, str, (char *)0); + PERROR("shell", theShell); + exit(1); + } + if (pid > 0) + while (wait((void *) &Status) != pid) + ; + (void) Signal(SIGINT, old1); + (void) Signal(SIGQUIT, old2); + if (pid == -1) { + PERROR("shell", "Try again later"); + } + return NOERR; +} /* shell */ + + + + +/* + * Send new user information (re-login) + */ +int do_user(int argc, char **argv) +{ + char acct[80]; + int n, aflag = 0; + string str; + + if (argc < 2) + argv = re_makeargv("(username) ", &argc); + if (argc > 4) { + return USAGE; + } + (void) sprintf(str, "USER %s", argv[1]); + n = command(str); + if (n == CONTINUE) { + if (argc < 3 ) + argv[2] = Getpass("Password: "), argc++; + (void) sprintf(str, "PASS %s", argv[2]); + n = command(str); + } + if (n == CONTINUE) { + if (argc < 4) { + (void) printf("Account: "); (void) fflush(stdout); + (void) FGets(acct, stdin); + acct[strlen(acct) - 1] = '\0'; + argv[3] = acct; argc++; + } + (void) sprintf(str, "ACCT %s", argv[3]); + n = command(str); + aflag++; + } + if (n != COMPLETE) { + (void) fprintf(stdout, "Login failed.\n"); + logged_in = 0; + return (0); + } + if (!aflag && argc == 4) { + (void) sprintf(str, "ACCT %s", argv[3]); + (void) command(str); + } + logged_in = 1; + CheckRemoteSystemType(0); + return NOERR; +} /* do_user */ + + + + +/* + * Print working directory. + */ +/*ARGSUSED*/ +int pwd(int argc, char **argv) +{ + (void) verbose_command("PWD"); + return NOERR; +} /* pwd */ + + + + +/* + * Make a directory. + */ +int makedir(int argc, char **argv) +{ + string str; + + if (argc < 2) + argv = re_makeargv("(directory-name) ", &argc); + if (argc < 2) { + return USAGE; + } + (void) sprintf(str, "MKD %s", argv[1]); + (void) command(str); + return NOERR; +} /* makedir */ + + + + +/* + * Remove a directory. + */ +int removedir(int argc, char **argv) +{ + string str; + if (argc < 2) + argv = re_makeargv("(directory-name) ", &argc); + if (argc < 2) { + return USAGE; + } + if (rem_glob_one(argv[1]) == 0) { + (void) sprintf(str, "RMD %s", argv[1]); + (void) command(str); + } + return NOERR; +} /* removedir */ + + + + +/* + * Send a line, verbatim, to the remote machine. + */ +int quote(int argc, char **argv) +{ + int i, tmpverbose; + string str; + + if (argc < 2) + argv = re_makeargv("(command line to send) ", &argc); + if (argc < 2) { + return USAGE; + } + str[0] = 0; + if (*argv[0] == 's') /* Command was 'site' instead of 'quote.' */ + (void) Strncpy(str, "site "); + (void) Strncat(str, argv[1]); + for (i = 2; i < argc; i++) { + (void) Strncat(str, " "); + (void) Strncat(str, argv[i]); + } + tmpverbose = verbose; + verbose = V_VERBOSE; + if (command(str) == PRELIM) { + while (getreply(0) == PRELIM); + } + verbose = tmpverbose; + return NOERR; +} /* quote */ + + + + +/* + * Ask the other side for help. + */ +int rmthelp(int argc, char **argv) +{ + string str; + + if (argc == 1) (void) verbose_command("HELP"); + else { + (void) sprintf(str, "HELP %s", argv[1]); + (void) verbose_command(str); + } + return NOERR; +} /* rmthelp */ + + + + +/* + * Terminate session and exit. + */ +/*ARGSUSED*/ +int quit(int argc, char **argv) +{ + close_up_shop(); + trim_log(); + exit(0); +} /* quit */ + + + +void close_streams(int wantShutDown) +{ + if (cout != NULL) { + if (wantShutDown) + (void) shutdown(fileno(cout), 1+1); + (void) fclose(cout); + cout = NULL; + } + if (cin != NULL) { + if (wantShutDown) + (void) shutdown(fileno(cin), 1+1); + (void) fclose(cin); + cin = NULL; + } +} /* close_streams */ + + + + +/* + * Terminate session, but don't exit. + */ +/*ARGSUSED*/ +int disconnect(int argc, char **argv) +{ +#ifdef SYSLOG + syslog (LOG_INFO, "%s disconnected from %s.", uinfo.username, hostname); +#endif + + (void) command("QUIT"); + close_streams(0); + if (logged_in) + UpdateRecentSitesList(hostname, cwd); + hostname[0] = cwd[0] = 0; + logged_in = connected = 0; + data = -1; + macnum = 0; + return NOERR; +} /* disconnect */ + + + +void +close_up_shop(void) +{ + static int only_once = 0; + if (only_once++ > 0) + return; + if (connected) + (void) disconnect(0, NULL); + WriteRecentSitesFile(); + if (logf != NULL) { + (void) fclose(logf); + logf = NULL; + } +} /* close_up_shop */ + + + + +/* + * Glob a local file name specification with + * the expectation of a single return value. + * Can't control multiple values being expanded + * from the expression, we return only the first. + */ +int globulize(char **cpp) +{ + char **globbed; + + (void) LocalPath(*cpp); + globbed = glob(*cpp); + if (globerr != NULL) { + (void) printf("%s: %s\n", *cpp, globerr); + if (globbed) { + blkfree(globbed); + Free(globbed); + } + return (0); + } + if (globbed) { + *cpp = *globbed++; + /* don't waste too much memory */ + if (*globbed) { + blkfree(globbed); + Free(globbed); + } + } + return (1); +} /* globulize */ + + + +/* change directory to perent directory */ +/*ARGSUSED*/ +int cdup(int argc, char **argv) +{ + (void) _cd("CDUP"); + return NOERR; +} /* cdup */ + + +/* show remote system type */ +/*ARGSUSED*/ +int syst(int argc, char **argv) +{ + (void) verbose_command("SYST"); + return NOERR; +} /* syst */ + + + + +int make_macro(char *name, FILE *fp) +{ + char *tmp; + char *cp; + string str; + size_t len; + int i; + + if (macnum == MAXMACROS) { + (void) fprintf(stderr, "Limit of %d macros have already been defined.\n", MAXMACROS); + return -1; + } + + /* Make sure macros have unique names. If 'init' was attempted to be + * redefined, just return, since it was probably cmdOpen() in a redial + * mode which tried to define it again. + */ + for (i = 0; i<macnum; i++) { + if (strncmp(name, macros[i].mac_name, (size_t)8) == 0) { + if (parsing_rc) { + /* Just shut up and read in the macro, but don't save it, + * because we already have it. + */ + while ((cp = FGets(str, fp)) != NULL) { + /* See if we have a 'blank' line: just whitespace. */ + while (*cp && isspace(*cp)) ++cp; + if (!*cp) + break; + } + } else + (void) fprintf(stderr, + "There is already a macro named '%s.'\n", name); + return -1; + } + } + (void) strncpy(macros[macnum].mac_name, name, (size_t)8); + if (macnum == 0) + macros[macnum].mac_start = macbuf; + else + macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; + tmp = macros[macnum].mac_start; + while (1) { + cp = FGets(str, fp); + if (cp == NULL) { + /* + * If we had started a macro, we will say it is + * okay to skip the blank line delimiter if we + * are at the EOF. + */ + if (tmp > macros[macnum].mac_start) + goto endmac; + (void) fprintf(stderr, "No text supplied for macro \"%s.\"\n", name); + } + /* see if we have a 'blank' line: just whitespace. */ + while (*cp && isspace(*cp)) ++cp; + if (*cp == '\0') { + /* Blank line; end this macro. */ +endmac: + macros[macnum++].mac_end = tmp; + return 0; + } + /* Add the text of this line to the macro. */ + len = strlen(cp) + 1; /* we need the \0 too. */ + if (tmp + len >= macbuf + MACBUFLEN) { + (void) fprintf(stderr, "Macro \"%s\" not defined -- %d byte buffer exceeded.\n", name, MACBUFLEN); + return -1; + } + (void) strcpy(tmp, cp); + tmp += len; + } +} /* make_macro */ + + + + +int macdef(int argc, char **argv) +{ + if (argc < 2) + argv = re_makeargv("(macro name) ", &argc); + if (argc != 2) { + (void) domacro(0, NULL); + return USAGE; + } + (void) printf("Enter macro line by line, terminating it with a blank line\n"); + (void) make_macro(argv[1], stdin); + return NOERR; +} /* macdef */ + + + + +int domacro(int argc, char **argv) +{ + register int i, j; + register char *cp1, *cp2; + int count = 2, loopflg = 0; + string str; + struct cmd *c; + + if (argc < 2) { + /* print macros. */ + if (macnum == 0) + (void) printf("No macros defined.\n"); + else { + (void) printf("Current macro definitions:\n"); + for (i = 0; i < macnum; ++i) { + (void) printf("%s:\n", macros[i].mac_name); + cp1 = macros[i].mac_start; + cp2 = macros[i].mac_end; + while (cp1 < cp2) { + (void) printf(" > "); + while (cp1 < cp2 && *cp1) + putchar(*cp1++); + ++cp1; + } + } + } + if (argc == 0) return (NOERR); /* called from macdef(), above. */ + argv = re_makeargv("(macro to run) ", &argc); + } + if (argc < 2) { + return USAGE; + } + for (i = 0; i < macnum; ++i) { + if (!strncmp(argv[1], macros[i].mac_name, (size_t) 9)) { + break; + } + } + if (i == macnum) { + (void) printf("'%s' macro not found.\n", argv[1]); + return USAGE; + } + doingInitMacro = (strcmp(macros[i].mac_name, "init") == 0); + (void) Strncpy(str, line); +TOP: + cp1 = macros[i].mac_start; + while (cp1 != macros[i].mac_end) { + while (isspace(*cp1)) { + cp1++; + } + cp2 = line; + while (*cp1 != '\0') { + switch(*cp1) { + case '\\': + *cp2++ = *++cp1; + break; + case '$': + if (isdigit(*(cp1+1))) { + j = 0; + while (isdigit(*++cp1)) { + j = 10*j + *cp1 - '0'; + } + cp1--; + if (argc - 2 >= j) { + (void) strcpy(cp2, argv[j+1]); + cp2 += strlen(argv[j+1]); + } + break; + } + if (*(cp1+1) == 'i') { + loopflg = 1; + cp1++; + if (count < argc) { + (void) strcpy(cp2, argv[count]); + cp2 += strlen(argv[count]); + } + break; + } + /* intentional drop through */ + default: + *cp2++ = *cp1; + break; + } + if (*cp1 != '\0') { + cp1++; + } + } + *cp2 = '\0'; + makeargv(); + c = getcmd(margv[0]); + if ((c == (struct cmd *) -1) && !parsing_rc) { + (void) printf("?Ambiguous command\n"); + } else if (c == NULL && !parsing_rc) { + (void) printf("?Invalid command\n"); + } else if (c->c_conn && !connected) { + (void) printf("Not connected.\n"); + } else { + if (IS_VVERBOSE) + (void) printf("%s\n",line); + if ((*c->c_handler)(margc, margv) == USAGE) + cmd_usage(c); + (void) strcpy(line, str); + makeargv(); + argc = margc; + argv = margv; + } + if (cp1 != macros[i].mac_end) { + cp1++; + } + } + if (loopflg && ++count < argc) { + goto TOP; + } + doingInitMacro = 0; + return NOERR; +} /* domacro */ + + + +/* + * get size of file on remote machine + */ +int sizecmd(int argc, char **argv) +{ + string str; + + if (argc < 2) + argv = re_makeargv("(remote-file) ", &argc); + if (argc < 2) { + return USAGE; + } + if (rem_glob_one(argv[1]) == 0) { + (void) sprintf(str, "SIZE %s", argv[1]); + (void) verbose_command(str); + } + return NOERR; +} /* sizecmd */ + + + + +/* + * get last modification time of file on remote machine + */ +int modtime(int argc, char **argv) +{ + int overbose; + string str; + + if (argc < 2) + argv = re_makeargv("(remote-file) ", &argc); + if (argc < 2) { + return USAGE; + } + if (rem_glob_one(argv[1]) == 0) { + overbose = verbose; + if (debug == 0) + verbose = V_QUIET; + (void) sprintf(str, "MDTM %s", argv[1]); + if (command(str) == COMPLETE) { + int yy, mo, day, hour, min, sec; + (void) sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", + &yy, &mo, &day, &hour, &min, &sec); + /* might want to print this in local time */ + (void) printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1], + mo, day, yy, hour, min, sec); + } else + (void) fputs(reply_string, stdout); + verbose = overbose; + } + return NOERR; +} /* modtime */ + + + +int lookup(int argc, char **argv) +{ + int i, j, by_name, result = NOERR; + struct hostent *host; /* structure returned by gethostbyaddr() */ + extern int h_errno; +#ifdef BAD_INETADDR + struct in_addr addr; /* address in host order */ +# define ADDR addr.s_addr +#else + unsigned long addr; /* address in host order */ +# define ADDR addr +#endif + + if (argc < 2) + argv = re_makeargv("(sitename) ", &argc); + if (argc < 2) { + return USAGE; + } + + lasthostname[0] = 0; + for (i=1; i<argc; i++) { + /* does the argument look like an address? */ + if (4 == sscanf (argv[i], "%d.%d.%d.%d", &j, &j, &j, &j)) { + /* ip */ + addr = inet_addr (argv[i]); + if (ADDR == 0xffffffff) { + (void) fprintf(stderr, "## could not convert \"%s\" into a valid IP address.\n", argv[i]); + continue; + } + host = gethostbyaddr ((char *) &ADDR, 4, AF_INET); + by_name = 0; + } else { + /* name */ + host = gethostbyname (argv[i]); + by_name = 1; + } + if (host == NULL) { + if (NOT_VQUIET) { + /* gethostxxx error */ + if (h_errno == HOST_NOT_FOUND) { + (void) printf("%s: lookup error (%d).\n", + argv[i], h_errno); + result = h_errno; + } else { + (void) printf("%s \"%s\"\n", + (by_name==0 ? "unknown address" : "unknown host"), + argv[i]); + result = + h_errno != 0 ? h_errno : + -1; + } + } + } else { + if (*host->h_name) + (void) Strncpy(lasthostname, host->h_name); + for (j=0; host->h_aliases[j] != NULL; j++) { + if (strlen(host->h_aliases[j]) > + strlen(host->h_name) && + strstr(host->h_aliases[j],host->h_name) != NULL) + (void) Strncpy(lasthostname,host->h_aliases[j]); + } + if (NOT_VQUIET) { + (void) printf("%-32s ", *host->h_name ? host->h_name : "???"); + if (*host->h_addr_list) { + unsigned long horder; + + horder = ntohl (*(unsigned long *) *(char **)host->h_addr_list); + (void) printf ("%lu.%lu.%lu.%lu\n", + (horder >> 24), + (horder >> 16) & 0xff, + (horder >> 8) & 0xff, + horder & 0xff); + } + else (void) printf("???\n"); + } + } + } /* loop thru all sites */ + return result; +} /* lookup */ + + + + +int getlocalhostname(char *host, size_t size) +{ + int oldv, r; + char *argv[2]; + char domain[64]; + +#ifdef HOSTNAME + (void) strncpy(host, HOSTNAME, size); + return NOERR; +#else + host[0] = '\0'; + if ((r = gethostname(host, size)) == 0) { + if (host[0] == '\0') { + (void) fprintf(stderr, +"Could not determine the hostname. Re-compile with HOSTNAME defined\n\ +to be the full name of your hostname.\n"); + exit(1); + } + oldv = verbose; + verbose = V_QUIET; + argv[0] = "lookup"; + (void) sprintf(line, "lookup %s", host); + (void) makeargv(); + if (lookup(margc, margv) == 0 && lasthostname[0]) { + (void) _Strncpy(host, lasthostname, size); + domain[0] = '\0'; +#ifdef HAS_DOMAINNAME + /* getdomainname() returns just the domain name, without a + * preceding period. For example, on "cse.unl.edu", it would + * return "unl.edu". + * + * SunOS note: getdomainname will return an empty string if + * this machine isn't on NIS. + */ + (void) getdomainname(domain, sizeof(domain) - 1); +#endif +#ifdef DOMAIN_NAME + (void) Strncpy(domain, DOMAIN_NAME); +#endif + if (index(host, '.') == NULL) { + /* If the hostname has periods we'll assume that the + * it includes the domain name already. Some gethostname()s + * return the whole host name, others just the machine name. + * If we have just the machine name and we successfully + * found out the domain name (from above), we'll append + * the domain to the machine to get a full hostname. + */ + if (domain[0]) { + (void) _Strncat(host, ".", size); + (void) _Strncat(host, domain, size); + } else { + fprintf(stderr, +"WARNING: could not determine full host name (have: '%s').\n\ +The program should be re-compiled with DOMAIN_NAME defined to be the\n\ +domain name, i.e. -DDOMAIN_NAME=\\\"unl.edu\\\"\n\n", + host); + } + } + } + verbose = oldv; + } + return r; +#endif +} /* getlocalhostname */ + + + + +/* + * show status on remote machine + */ +int rmtstatus(int argc, char **argv) +{ + string str; + + if (argc > 1) { + (void) sprintf(str, "STAT %s" , argv[1]); + (void) verbose_command(str); + } else (void) verbose_command("STAT"); + return NOERR; +} /* rmtstatus */ + + + + +/* + * create an empty file on remote machine. + */ +int create(int argc, char **argv) +{ + string str; + FILE *ftemp; + + if (argc < 2) + argv = re_makeargv("(remote-file) ", &argc); + if (argc < 2) { + return USAGE; + } + (void) tmp_name(str); + ftemp = fopen(str, "w"); + /* (void) fputc('x', ftemp); */ + (void) fclose(ftemp); + creating = 1; + (void) sendrequest("STOR", str, argv[1]); + creating = 0; + (void) unlink(str); + return NOERR; +} /* create */ + + + + +/* show version info */ +/*ARGSUSED*/ +int show_version(int argc, char **argv) +{ + char *DStrs[80]; + int nDStrs = 0, i, j; + + (void) printf("%-30s %s\n", "NcFTP Version:", version); + (void) printf("%-30s %s\n", "Author:", + "Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu)."); + +/* Now entering CPP hell... */ +#ifdef __DATE__ + (void) printf("%-30s %s\n", "Compile Date:", __DATE__); +#endif + (void) printf("%-30s %s (%s)\n", "Operating System:", +#ifdef System + System, +#else +# ifdef unix + "UNIX", +# else + "??", +# endif +#endif +#ifdef SYSV + "SYSV"); +#else +# ifdef BSD + "BSD"); +# else + "neither BSD nor SYSV?"); +# endif +#endif + + /* Show which CPP symbols were used in compilation. */ +#ifdef __GNUC__ + DStrs[nDStrs++] = "__GNUC__"; +#endif +#ifdef RINDEX + DStrs[nDStrs++] = "RINDEX"; +#endif +#ifdef CURSES + DStrs[nDStrs++] = "CURSES"; +#endif +#ifdef NO_CURSES_H + DStrs[nDStrs++] = "NO_CURSES_H"; +#endif +#ifdef HERROR + DStrs[nDStrs++] = "HERROR"; +#endif +#ifdef U_WAIT + DStrs[nDStrs++] = "U_WAIT"; +#endif +#if defined(NO_CONST) || defined(const) + DStrs[nDStrs++] = "NO_CONST"; +#endif +#ifdef NO_FORMATTING + DStrs[nDStrs++] = "NO_FORMATTING"; +#endif +#ifdef DONT_TIMESTAMP + DStrs[nDStrs++] = "DONT_TIMESTAMP"; +#endif +#ifdef GETPASS + DStrs[nDStrs++] = "GETPASS"; +#endif +#ifdef HAS_GETCWD + DStrs[nDStrs++] = "HAS_GETCWD"; +#endif +#ifdef GETCWDSIZET + DStrs[nDStrs++] = "GETCWDSIZET"; +#endif +#ifdef HAS_DOMAINNAME + DStrs[nDStrs++] = "HAS_DOMAINNAME"; +#endif +#ifdef DOMAIN_NAME + DStrs[nDStrs++] = "DOMAIN_NAME"; +#endif +#ifdef Solaris + DStrs[nDStrs++] = "Solaris"; +#endif +#ifdef USE_GETPWUID + DStrs[nDStrs++] = "USE_GETPWUID"; +#endif +#ifdef HOSTNAME + DStrs[nDStrs++] = "HOSTNAME"; +#endif +#ifdef SYSDIRH + DStrs[nDStrs++] = "SYSDIRH"; +#endif +#ifdef SYSSELECTH + DStrs[nDStrs++] = "SYSSELECTH"; +#endif +#ifdef TERMH + DStrs[nDStrs++] = "TERMH"; +#endif +#ifdef NO_UNISTDH + DStrs[nDStrs++] = "NO_UNISTDH"; +#endif +#ifdef NO_STDLIBH + DStrs[nDStrs++] = "NO_STDLIBH"; +#endif +#ifdef SYSLOG + DStrs[nDStrs++] = "SYSLOG"; +#endif +#ifdef BAD_INETADDR + DStrs[nDStrs++] = "BAD_INETADDR"; +#endif +#ifdef SGTTYB + DStrs[nDStrs++] = "SGTTYB"; +#endif +#ifdef TERMIOS + DStrs[nDStrs++] = "TERMIOS"; +#endif +#ifdef STRICT_PROTOS + DStrs[nDStrs++] = "STRICT_PROTOS"; +#endif +#ifdef dFTP_PORT + DStrs[nDStrs++] = "dFTP_PORT"; +#endif +#ifdef BROKEN_MEMCPY + DStrs[nDStrs++] = "BROKEN_MEMCPY"; +#endif +#ifdef READLINE + DStrs[nDStrs++] = "READLINE"; +#endif +#ifdef GETLINE + DStrs[nDStrs++] = "GETLINE"; +#endif +#ifdef _POSIX_SOURCE + DStrs[nDStrs++] = "_POSIX_SOURCE"; +#endif +#ifdef _XOPEN_SOURCE + DStrs[nDStrs++] = "_XOPEN_SOURCE"; +#endif +#ifdef NO_TIPS + DStrs[nDStrs++] = "NO_TIPS"; +#endif +#ifdef GZCAT + DStrs[nDStrs++] = "GZCAT"; +#endif +#ifdef LINGER + DStrs[nDStrs++] = "LINGER"; +#endif +#ifdef TRY_NOREPLY + DStrs[nDStrs++] = "TRY_NOREPLY"; +#endif +#ifdef NO_UTIMEH + DStrs[nDStrs++] = "NO_UTIMEH"; +#endif +#ifdef DB_ERRS + DStrs[nDStrs++] = "DB_ERRS"; +#endif +#ifdef NO_VARARGS + DStrs[nDStrs++] = "NO_VARARGS"; +#endif +#ifdef NO_STDARGH + DStrs[nDStrs++] = "NO_STDARGH"; +#endif +#ifdef NO_MKTIME + DStrs[nDStrs++] = "NO_MKTIME"; +#endif +#ifdef NO_STRSTR + DStrs[nDStrs++] = "NO_STRSTR"; +#endif +#ifdef NO_STRFTIME + DStrs[nDStrs++] = "NO_STRFTIME"; +#endif +#ifdef NO_RENAME + DStrs[nDStrs++] = "NO_RENAME"; +#endif +#ifdef TRY_ABOR + DStrs[nDStrs++] = "TRY_ABOR"; +#endif +#ifdef GATEWAY + DStrs[nDStrs++] = "GATEWAY"; +#endif +#ifdef SOCKS + DStrs[nDStrs++] = "SOCKS"; +#endif +#ifdef NET_ERRNO_H + DStrs[nDStrs++] = "NET_ERRNO_H"; +#endif +#ifdef PASSIVEMODE + DStrs[nDStrs++] = "PASSIVEMODE"; +#endif + + +/* DONE with #ifdefs for now! */ + + (void) printf ("\nCompile Options:\n"); + for (i=j=0; i<nDStrs; i++) { + if (j == 0) + (void) printf(" "); + (void) printf("%-15s", DStrs[i]); + if (++j == 4) { + j = 0; + (void) putchar('\n'); + } + } + if (j != 0) + (void) putchar('\n'); + +#ifdef MK + (void) printf("\nMK: %s\n", MK); +#endif /* MK */ + + (void) printf("\nDefaults:\n"); + (void) printf("\ + Xfer Buf Size: %8d Debug: %d MPrompt: %d Verbosity: %d\n\ + Prompt: %s Pager: %s ZCat: %s\n\ + Logname: %s Logging: %d Type: %s Cmd Len: %d\n\ + Recv Line Len: %d #Macros: %d Macbuf: %d Auto-Binary: %d\n\ + Recent File: %s Recent On: %d nRecents: %d\n\ + Redial Delay: %d Anon Open: %d New Mail Message: \"%s\"\n", + MAX_XFER_BUFSIZE, dDEBUG, dMPROMPT, dVERBOSE, + dPROMPT, dPAGER, ZCAT, + dLOGNAME, dLOGGING, dTYPESTR, CMDLINELEN, + RECEIVEDLINELEN, MAXMACROS, MACBUFLEN, dAUTOBINARY, + dRECENTF, dRECENT_ON, dMAXRECENTS, + dREDIALDELAY, dANONOPEN, NEWMAILMESSAGE + ); +#ifdef GATEWAY + (void) printf("\ + Gateway Login: %s\n", dGATEWAY_LOGIN); +#endif + return NOERR; +} /* show_version */ + + + +void PurgeLineBuffer(void) +{ + register struct lslist *a, *b; + + for (a = lshead; a != NULL; ) { + b = a->next; + if (a->string) + free(a->string); /* free string */ + Free(a); /* free node */ + a = b; + } + lshead = lstail = NULL; +} /* PurgeLineBuffer */ + + + + +/*ARGSUSED*/ +int ShowLineBuffer(int argc, char **argv) +{ + register struct lslist *a = lshead; + int pagemode; + FILE *fp; + Sig_t oldintp; + + if (a == NULL) + return CMDERR; + pagemode= (**argv) == 'p' && pager[0] == '|'; + if (pagemode) { + fp = popen(pager + 1, "w"); + if (!fp) { + PERROR("ShowLineBuffer", pager + 1); + return CMDERR; + } + } else + fp = stdout; + oldintp = Signal(SIGPIPE, SIG_IGN); + while (a) { + if (a->string) + (void) fprintf(fp, "%s\n", a->string); + a = a->next; + } + if (pagemode) + (void) pclose(fp); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + return NOERR; +} /* ShowLineBuffer */ + + + + +#if LIBMALLOC != LIBC_MALLOC +/*ARGSUSED*/ +int MallocStatusCmd(int argc, char **argv) +{ +#if (LIBMALLOC == FAST_MALLOC) + struct mallinfo mi; + + mi = mallinfo(); + printf("\ +total space in arena: %d\n\ +number of ordinary blocks: %d\n\ +number of small blocks: %d\n\ +number of holding blocks: %d\n\ +space in holding block headers: %d\n\ +space in small blocks in use: %d\n\ +space in free small blocks: %d\n\ +space in ordinary blocks in use: %d\n\ +space in free ordinary blocks: %d\n\ +cost of enabling keep option: %d\n", + mi.arena, + mi.ordblks, + mi.smblks, + mi.hblks, + mi.hblkhd, + mi.usmblks, + mi.fsmblks, + mi.uordblks, + mi.fordblks, + mi.keepcost + ); +#else +#if (LIBMALLOC == DEBUG_MALLOC) + printf("malloc_chain_check: %d\n\n", malloc_chain_check(0)); + if (argc > 1) + malloc_dump(1); + printf("malloc_inuse: %lu\n", malloc_inuse(NULL)); +#else + printf("Nothing to report.\n"); +#endif /* (LIBMALLOC == DEBUG_MALLOC) */ +#endif /* (LIBMALLOC == FAST_MALLOC) */ + + return (0); +} /* MallocStatusCmd */ +#endif /* LIBMALLOC */ + + + + +/*ARGSUSED*/ +int unimpl(int argc, char **argv) +{ + if (!parsing_rc) + (void) printf("%s: command not supported. (and probably won't ever be).\n", argv[0]); + return (NOERR); +} /* unimpl */ + +#ifdef PASSIVEMODE +int setpassive(int argc, char **argv) +{ + passivemode = !passivemode; + printf( "Passive mode %s.\n", (passivemode ? "ON" : "OFF") ); + return NOERR; +} +#endif + + +/* eof cmds.c */ diff --git a/usr.bin/ncftp/cmds.h b/usr.bin/ncftp/cmds.h new file mode 100644 index 000000000000..fe1cacc494b2 --- /dev/null +++ b/usr.bin/ncftp/cmds.h @@ -0,0 +1,134 @@ +/* cmds.h */ + +#ifndef _cmd_h_ +#define _cmd_h_ + +/* $RCSfile: cmds.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/07/09 10:58:19 $ + */ + +/* Verbosity levels. */ +#define V_QUIET -1 +#define V_ERRS 0 +#define V_TERSE 1 +#define V_VERBOSE 2 +#define V_IMPLICITCD 4 +#define IS_VQUIET (verbose <= V_QUIET) +#define IS_VERRS (verbose == V_ERRS) +#define IS_VTERSE (verbose == V_TERSE) +#define IS_VVERBOSE (verbose == V_VERBOSE) +#define NOT_VQUIET (verbose > V_QUIET) + +/* Open modes. */ +#define OPEN_A 1 +#define OPEN_U 0 + +#define LS_FLAGS_AND_FILE '\1' + +/* Possible values returned by GetDateAndTime. */ +#define SIZE_UNKNOWN (-1L) +#define MDTM_UNKNOWN (0L) + +/* Command result codes. */ +#define USAGE (88) +#define NOERR (0) +#define CMDERR (-1) + +/* + * Format of command table. + */ +struct cmd { + char *c_name; /* name of command */ + char c_conn; /* must be connected to use command */ + char c_hidden; /* a hidden command or alias (won't show up in help) */ + int (*c_handler)(int, char **); /* function to call */ + char *c_help; /* help string */ + char *c_usage; /* usage string or NULL, to ask the function itself. */ +}; + +#define NCMDS ((int) ((sizeof (cmdtab) / sizeof (struct cmd)) - 1)) + +struct macel { + char mac_name[9]; /* macro name */ + char *mac_start; /* start of macro in macbuf */ + char *mac_end; /* end of macro in macbuf */ +}; + +struct types { + char *t_name; + char *t_mode; + int t_type; + char *t_arg; +}; + +struct lslist { + char *string; + struct lslist *next; +}; + +int settype(int argc, char **argv); +int _settype(char *typename); +int setbinary(int argc, char **argv); +int setascii(int argc, char **argv); +int put(int argc, char **argv); +int mput(int argc, char **argv); +int rem_glob_one(char *pattern); +int get(int argc, char **argv); +void mabort SIG_PARAMS; +int mget(int argc, char **argv); +char *remglob(char *argv[]); +int setverbose(int argc, char **argv); +int setprompt(int argc, char **argv); +int setdebug(int argc, char **argv); +void fix_options(void); +int cd(int argc, char **argv); +int implicit_cd(char *dir); +int _cd(char *dir); +int lcd(int argc, char **argv); +int do_delete(int argc, char **argv); +int mdelete(int argc, char **argv); +int renamefile(int argc, char **argv); +int ls(int argc, char **argv); +int shell(int argc, char **argv); +int do_user(int argc, char **argv); +int pwd(int argc, char **argv); +int makedir(int argc, char **argv); +int removedir(int argc, char **argv); +int quote(int argc, char **argv); +int rmthelp(int argc, char **argv); +int quit(int argc, char **argv); +void close_streams(int wantShutDown); +int disconnect(int argc, char **argv); +void close_up_shop(void); +int globulize(char **cpp); +int cdup(int argc, char **argv); +int syst(int argc, char **argv); +int make_macro(char *name, FILE *fp); +int macdef(int argc, char **argv); +int domacro(int argc, char **argv); +int sizecmd(int argc, char **argv); +int modtime(int argc, char **argv); +int lookup(int argc, char **argv); +int rmtstatus(int argc, char **argv); +int create(int argc, char **argv); +int getlocalhostname(char *host, size_t size); +int show_version(int argc, char **argv); +void PurgeLineBuffer(void); +int ShowLineBuffer(int argc, char **argv); +int MallocStatusCmd(int argc, char **argv); +int unimpl(int argc, char **argv); +long GetDateSizeFromLSLine(char *fName, unsigned long *mod_time); +long GetDateAndSize(char *fName, unsigned long *mod_time); +int SetTypeByNumber(int i); +#ifdef PASSIVEMODE +int setpassive(int argc, char **argv); +#endif + + +/* In util.c: */ +void cmd_help(struct cmd *c); +void cmd_usage(struct cmd *c); +struct cmd *getcmd(char *name); + +#endif /* _cmd_h_ */ diff --git a/usr.bin/ncftp/cmdtab.c b/usr.bin/ncftp/cmdtab.c new file mode 100644 index 000000000000..d4e161f3fcc5 --- /dev/null +++ b/usr.bin/ncftp/cmdtab.c @@ -0,0 +1,282 @@ +/* cmdtab.c */ + +/* $RCSfile: cmdtab.c,v $ + * $Revision: 14020.11 $ + * $Date: 93/07/09 11:04:56 $ + */ + +#include "sys.h" +#include "util.h" +#include "cmds.h" +#include "main.h" +#include "ftp.h" +#include "ftprc.h" +#include "glob.h" +#include "open.h" +#include "set.h" +#include "copyright.h" + +#define REMOTEFILE " remote-file-name" +#define REMOTEFILES " remote-file-names and/or UNIX-style-wildcards" +#define LOCALFILE " local-file-name" +#define LOCALFILES " local-file-names and/or UNIX-style-wildcards" +#define LDIRNAME " local-directory-name" +#define RMTDIRNAME " remote-directory-name" +#define EMPTYSTR "" +#define TOGGLE " [on | off] (no argument toggles the switch)" + +#define BINARYHELP "transfer files as binary files, without CR/LF translation" +#define BINARYUSAGE EMPTYSTR + +#define CHDIRHELP "changes the current remote working directory" +#define CHDIRUSAGE RMTDIRNAME + +#define CLOSEHELP "closes FTP connection to current remote host" +#define CLOSEUSAGE EMPTYSTR + +#define DELETEHELP "deletes the specified file on the remote host" +#define DELETEUSAGE REMOTEFILE + +#define DIRUSAGE " \ +[flags] [remote-items] [>outfile or \"|pipecmd [cmd-args]\"]\n\ + Note that there must be no whitespace between > and outfile, or | and\n\ + pipecmd, and if the pipe-command needs arguments, you must enclose the\n\ + whole thing with double quotes.\n\ +Examples:\n\ + dir -s\n\ + dir remoteFile\n\ + dir /pub/mac \"|head -20\"\n\ + dir -rtR file1 file2 dir1 >contents.txt" + +#define GETUSAGE " remote-file-name [local-file-name or |pipecommand]\n\ +Examples:\n\ + get myfile.txt\n\ + get MYFILE.ZIP myfile.zip\n\ + get myfile.txt |head\n\ + get myfile.txt \"|head -20\"\n\ + get ./help/newuser.txt (./newuser.txt will be local-file-name)\n\ + get ./help/newuser.txt ./docs/newbie.help\n\ + get my*.txt (pseudo-filename-completion if match is unique, i.e. myfile.txt)" + +#define HELPHELP "shows commands, and optionally tell you how to use a specific one" +#define HELPUSAGE " [command-name | showall (shows hidden commands) | helpall" + +#define LSHELP "prints remote directory contents (short-mode)" +#define LSUSAGE " \ +[flags] [remote-items] [>outfile or \"|pipecmd [cmd-args]\"]\n\ + Note that there must be no whitespace between > and outfile, or | and\n\ + pipecmd, and if the pipe-command needs arguments, you must enclose the\n\ + whole thing with double quotes.\n\ +Examples:\n\ + ls -s\n\ + ls remoteFile\n\ + ls /pub/mac \"|head -20\"\n\ + ls -lrtR file1 file2 dir1 >contents.txt" + +#define OPENHELP "connects to a new remote host, and optionally fetches a file\n\ + or sets the current remote working directory" +#define OPENUSAGE " \ +[-a | -u] [-i] [-p N] [-r [-d N] [-g N]] hostname[:pathname]\n\ + -a : Open anonymously (this is the default).\n\ + -u : Open, specify user/password.\n\ + -i : Ignore machine entry in your .netrc.\n\ + -p N : Use port #N for connection.\n\ + -r : \"Redial\" until connected.\n\ + -d N : Redial, pausing N seconds between tries.\n\ + -g N : Redial, giving up after N tries.\n\ + :path : Open site, then retrieve file \"path.\" WWW-style paths are\n\ + also acceptable, i.e. 'ftp://cse.unl.edu/mgleason/README.'" + +#define PAGEHELP "view a file on the remote host with your $PAGER" +#define PAGEUSAGE REMOTEFILE + +#ifdef PASSIVEMODE +#define PASSIVEHELP "enter passive transfer mode" +#endif + + +#define PDIRUSAGE " [flags] [remote-files]" + +#define PUTHELP "sends a local file to the current remote host" +#define PUTUSAGE " local-file-name [remote-file-name]" + +#define QUITHELP "quits the program" +#define QUITUSAGE EMPTYSTR + +#define RHELPHELP "asks the remote-server for help" +#define RHELPUSAGE " [help-topic (i.e. FTP command)]" + +#define UNIMPLHELP "this command is not supported" +#define UNIMPLUSAGE (NULL) + +struct cmd cmdtab[] = { + /* name ; must-be-connected ; hidden ; help-string ; usage-string */ + { "!", 0, 0, shell, + "spawns a shell for you to run other commands", + " [single-command-and-arguments]" }, + { "$", 0, 0, domacro, + "runs a macro previously defined in your NETRC, or with the macdef cmd", + "macro-number" }, + { "account", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "append", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "ascii", 1, 1, setascii, + "transfer files as text files, with proper CR/LF translation", + "" }, + { "bell", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "binary", 1, 1, setbinary, BINARYHELP, BINARYUSAGE }, + { "bye", 0, 1, quit, QUITHELP, QUITUSAGE }, + { "case", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "cd", 1, 0, cd, CHDIRHELP, CHDIRUSAGE }, + { "cdup", 1, 0, cdup, + "changes the current remote working directory to it's parent", + "" }, + { "chdir", 1, 1, cd, CHDIRHELP, CHDIRUSAGE }, + { "close", 1, 1, disconnect, CLOSEHELP, CLOSEUSAGE }, + { "connect", 0, 1, cmdOpen, OPENHELP, OPENUSAGE }, + { "cr", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "create", 1, 0, create, + "create an empty file on the remote host", + REMOTEFILE }, + { "delete", 1, 0, do_delete, DELETEHELP, DELETEUSAGE }, + { "debug", 0, 1, setdebug, + "to print debugging messages during execution of the program", + TOGGLE }, + { "dir", 1, 0, ls, + "prints remote directory contents (long-mode)", + DIRUSAGE }, + { "erase", 1, 1, do_delete, DELETEHELP, DELETEUSAGE }, + { "exit", 0, 1, quit, QUITHELP, QUITUSAGE }, + { "form", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "get", 1, 0, get, + "fetches a file from the current remote host", GETUSAGE }, + { "glob", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "hash", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "help", 0, 0, help, HELPHELP, HELPUSAGE }, + { "idle", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "image", 1, 1, setbinary, BINARYHELP, BINARYUSAGE }, + { "lcd", 0, 0, lcd, + "changes the current local directory", LDIRNAME }, + { "lookup", 0, 0, lookup, + "uses the name-server to tell you a host's IP number given it's\n\ + name, or it's name given it's IP number", + " hostname | host-IP-number" }, + { "ls", 1, 0, ls, LSHELP, LSUSAGE }, + { "macdef", 0, 0, macdef, + "defines a macro which is expanded when you use the $ command", + " new-macro-name" }, + { "mdelete", 1, 0, mdelete, + "deletes multiple files on the remote host", REMOTEFILES }, + { "mdir", 1, 1, ls, LSHELP, LSUSAGE }, +#if LIBMALLOC != LIBC_MALLOC + { "memchk", 0, 0, MallocStatusCmd, + "show debugging information about memory usage.", EMPTYSTR }, +#endif + { "mget", 1, 0, mget, + "fetches multiple files from the remote host", REMOTEFILES }, + { "mkdir", 1, 0, makedir, + "creates a new sub-directory on the current remote host", + RMTDIRNAME }, + { "mls", 1, 0, ls, LSHELP, LSUSAGE }, + { "mode", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "modtime", 1, 0, modtime, + "shows the last modification date for a remote file", + REMOTEFILE }, + { "more", 1, 1, get, PAGEHELP, PAGEUSAGE }, + { "mput", 1, 0, mput, + "sends multiple local files to the current remote host", + LOCALFILES }, + { "newer", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "nlist", 1, 1, ls, LSHELP, LSUSAGE }, + { "nmap", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "ntrans", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "open", 0, 0, cmdOpen, OPENHELP, OPENUSAGE }, + { "p", 1, 1, get, PAGEHELP, PAGEUSAGE }, +#ifdef PASSIVEMODE + { "passive", 0, 0, setpassive, PASSIVEHELP, EMPTYSTR }, +#endif + { "page", 1, 0, get, PAGEHELP, PAGEUSAGE }, + { "pdir", 1, 0, ls, + "view a remote directory listing (long mode) with your $PAGER", + PDIRUSAGE }, + { "pls", 1, 0, ls, + "view a remote directory listing (short mode) with your $PAGER", + PDIRUSAGE }, + { "predir", 1, 0, ShowLineBuffer, + "view the last remote directory listing with your $PAGER", + EMPTYSTR }, + { "prompt", 0, 1, setprompt, + "toggle interactive prompting on multiple commands", + TOGGLE }, + { "proxy", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "put", 1, 0, put, PUTHELP, PUTUSAGE }, + { "pwd", 1, 0, pwd, + "prints the name of the current remote directory", + EMPTYSTR }, + { "quit", 0, 0, quit, QUITHELP, QUITUSAGE }, + { "quote", 1, 0, quote, + "allows advanced users to directly enter FTP commands verbatim", + " FTP-commands" }, + { "redir", 1, 0, ShowLineBuffer, + "re-prints the last directory listing", + EMPTYSTR }, + { "reget", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "remotehelp", 1, 0, rmthelp, RHELPHELP, RHELPUSAGE }, + { "reset", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "restart", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "rm", 1, 1, do_delete, DELETEHELP, DELETEUSAGE }, + { "rstatus", 1, 0, rmtstatus, + "asks the remote-server for it's status", + EMPTYSTR }, + { "rhelp", 1, 1, rmthelp, RHELPHELP, RHELPUSAGE }, + { "rename", 1, 0, renamefile, + "changes the name of a file on the current remote host", + " old-name new-name" }, + { "rmdir", 1, 0, removedir, + "deletes a directory on the current remote host", + RMTDIRNAME }, + { "runique", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "send", 1, 1, put, PUTHELP, PUTUSAGE }, + { "sendport", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "show", 0, 0, do_show, + "prints the value of some or all program variables", + " all | variable-names" }, + { "set", 0, 0, set, + "changes the value of a program variable; for numeric/boolean\n\ + variables sets them to 1/true", + " variable-name [= new-value]" }, + { "site", 1, 0, quote, + "allows advanced users to send site-specific commands to the host", + " site-specific-commands\n\ +Example (to try on wuarchive.wustl.edu):\n\ + site locate emacs" }, + { "size", 1, 0, sizecmd, + "shows the size of a remote file", + REMOTEFILE }, + { "struct", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "sunique", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "system", 1, 0, syst, + "tells you what type of machine the current remote host is", + EMPTYSTR }, + { "tenex", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "umask", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE }, + { "unset", 0, 0, set, + "resets the value of a program variable to it's default state, or for\n\ + numeric/boolean variables, sets them to 0/false", + " variable-name" }, + { "user", 1, 0, do_user, + "lets you login as a new user (with appropriate password)", + " new-user-name [new-password]" }, + { "type", 1, 0, settype, + "changes the current file transfer method", + " ascii | binary | ebcdic | tenex" }, + { "verbose", 0, 0, setverbose, + "controls how many message the program prints in response to commands", + " -1 (quiet) | 0 (errs) | 1 (terse) | 2 (verbose)" }, + { "version", 0, 0, show_version, + "prints information about the program", + EMPTYSTR }, + { "?", 0, 1, help, HELPHELP, HELPUSAGE }, + { NULL, 0, 0, NULL, NULL, NULL } +}; + +/* eof cmdtab.c */ diff --git a/usr.bin/ncftp/copyright.h b/usr.bin/ncftp/copyright.h new file mode 100644 index 000000000000..4e8156cbf691 --- /dev/null +++ b/usr.bin/ncftp/copyright.h @@ -0,0 +1,30 @@ +/* Copyright.h */ + +/* $RCSfile: copyright.h,v $ + * $Revision: 14020.12 $ + * $Date: 93/06/02 13:43:03 $ + */ + +/* + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1992-1994 Mike Gleason, NCEMRSoft. + * Copyright (c) 1985, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions may not be sold for profit on physical + * media such as disks, tapes, and CD-ROMS, without expressed written + * permission. + */ + +#ifdef _main_c_ +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1992, 1993 by NCEMRSoft and Copyright (c) 1985, 1989 Regents of the University of California.\n All rights reserved.\n"; +#endif /* not lint */ +#endif /* _main_c_ */ + +/* eof copyright.h */ diff --git a/usr.bin/ncftp/defaults.h b/usr.bin/ncftp/defaults.h new file mode 100644 index 000000000000..24521959c3cc --- /dev/null +++ b/usr.bin/ncftp/defaults.h @@ -0,0 +1,130 @@ +/* Defaults.h: default values for ftp's common variables */ + +/* These are all surrounded by #ifndef blocks so you can just use + * the -D flag with your compiler (i.e. -DZCAT=\"/usr/local/bin/zcat\"). + */ + +#ifndef _DEFAULTS_H_ +#define _DEFAULTS_H_ + +/* $RCSfile: defaults.h,v $ + * $Revision: 14020.13 $ + * $Date: 93/07/09 10:58:27 $ + */ + +#ifndef NEWMAILMESSAGE /* For english speakers, "You have new mail." */ +#define NEWMAILMESSAGE "You have new mail." +#endif + +#ifndef ZCAT /* Usually "zcat," but use the full pathname */ + /* if possible. */ +# ifdef GZCAT /* If you said you had gnu's zcat, use it + * since it can do .Z files too. + */ + +# define ZCAT GZCAT +# else /* !GZCAT */ +# define ZCAT "zcat" +# endif /* ifdef GZCAT */ +#endif /* ifndef ZCAT */ + +#ifndef MAX_XFER_BUFSIZE +#define MAX_XFER_BUFSIZE 32768 +#endif + +#ifndef dANONOPEN /* 1 or 0, usually 1 */ +#define dANONOPEN 1 +#endif + +#ifndef dDEBUG /* 1 or 0, usually 0 */ +#define dDEBUG 0 +#endif + +#ifndef dMPROMPT /* Usually 1, I prefer 0... */ +#define dMPROMPT 0 +#endif + +#ifndef dVERBOSE /* V_QUIET, V_ERRS, V_TERSE, V_VERBOSE */ +#define dVERBOSE V_TERSE +#endif + +#ifndef dPROMPT /* short: "@Bftp@P>" */ + /* long: "@B@E @UNcFTP@P @B@M@D@P ->" */ +#define dPROMPT "@B@c@Mncftp@P>" /* new two line prompt */ +#endif + +#ifndef dPAGER /* if set to empty string, act like 'cat' */ +#define dPAGER "more" +#endif + +#ifndef dLOGNAME /* usu. put in the user's home directory. */ +#define dLOGNAME "~/.ftplog" +#endif + +#ifndef dRECENTF /* usu. put in the user's home directory. */ +#define dRECENTF "~/.ncrecent" +#endif + +#ifndef dMAXRECENTS /* limit to how many recent sites to save. */ +#define dMAXRECENTS 50 +#endif + +#ifndef dRECENT_ON /* Do you want the recent log on? */ + /* usually 1. */ +#define dRECENT_ON 1 +#endif + + /* Do you want logging on by default? */ +#ifndef dLOGGING /* usually 0 */ +#define dLOGGING 0 +#endif + +#ifndef dTYPE /* usually TYPE_A */ +#define dTYPE TYPE_A +#endif + +#ifndef dTYPESTR /* usually "ascii" */ +#define dTYPESTR "ascii" +#endif + +#ifndef dREDIALDELAY /* usu. 60 (seconds). */ +#define dREDIALDELAY 60 +#endif + +#ifndef CMDLINELEN +#define CMDLINELEN 256 +#endif + +#ifndef RECEIVEDLINELEN +#define RECEIVEDLINELEN 256 +#endif + +#ifndef MAXMACROS +#define MAXMACROS 16 +#endif + +#ifndef MACBUFLEN /* usually 4096. */ +#define MACBUFLEN 4096 +#endif + +/* Do you want binary transfers by default? */ +#ifndef dAUTOBINARY /* usually 1 */ +#define dAUTOBINARY 1 +#endif + +#ifndef dPROGRESS +#define dPROGRESS pr_philbar /* can be: pr_none, pr_percent, pr_philbar, + * or pr_kbytes + */ +#endif + +/* Default login name at gateway */ +#ifdef GATEWAY +# ifndef dGATEWAY_LOGIN +# define dGATEWAY_LOGIN "ftp" +# endif +#endif + +#endif /* _DEFAULTS_H_ */ + +/* eof */ diff --git a/usr.bin/ncftp/ftp.c b/usr.bin/ncftp/ftp.c new file mode 100644 index 000000000000..71f7abe2b5c2 --- /dev/null +++ b/usr.bin/ncftp/ftp.c @@ -0,0 +1,1880 @@ +/* ftp.c */ + +/* $RCSfile: ftp.c,v $ + * $Revision: 14020.12 $ + * $Date: 93/07/09 11:30:28 $ + */ + +#include "sys.h" + +#include <setjmp.h> +#include <sys/stat.h> +#include <sys/file.h> + +#ifndef AIX /* AIX-2.2.1 declares utimbuf in unistd.h */ +#ifdef NO_UTIMEH +struct utimbuf {time_t actime; time_t modtime;}; +#else +# include <utime.h> +#endif +#endif /*AIX*/ + +#ifdef SYSLOG +# include <syslog.h> +#endif + +/* You may need this for declarations of fd_set, etc. */ +#ifdef SYSSELECTH +# include <sys/select.h> +#else +#ifdef STRICT_PROTOS +#ifndef Select +extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +#endif +#endif +#endif + +#include <netinet/in.h> +#include <arpa/ftp.h> +#include <arpa/inet.h> +#include <arpa/telnet.h> +#include <signal.h> +#include <errno.h> +#ifdef NET_ERRNO_H +# include <net/errno.h> +#endif +#include <netdb.h> +#include <fcntl.h> +#include <pwd.h> +#include <ctype.h> +#include "util.h" +#include "ftp.h" +#include "cmds.h" +#include "main.h" +#include "ftprc.h" +#include "getpass.h" +#include "defaults.h" +#include "copyright.h" + +/* ftp.c globals */ +struct sockaddr_in hisctladdr; +struct sockaddr_in data_addr; +int data = -1; +int abrtflag = 0; +struct sockaddr_in myctladdr; +FILE *cin = NULL, *cout = NULL; +char *reply_string = NULL; +jmp_buf sendabort, recvabort; +int progress_meter = dPROGRESS; +int cur_progress_meter; +int sendport = -1; /* use PORT cmd for each data connection */ +int code; /* return/reply code for ftp command */ +string indataline; +int cpend; /* flag: if != 0, then pending server reply */ +char *xferbuf; /* buffer for local and remote I/O */ +size_t xferbufsize; /* size in bytes, of the transfer buffer. */ +long next_report; +long bytes; +long now_sec; +long file_size; +struct timeval start, stop; +int buffer_only = 0; /* True if reading into redir line + * buffer only (not echoing to + * stdout). + */ + +/* ftp.c externs */ +extern FILE *logf; +extern string anon_password; +extern longstring cwd, lcwd; +extern Hostname hostname; +extern int verbose, debug, macnum, margc; +extern int curtype, creating, toatty; +extern int options, activemcmd, paging; +extern int ansi_escapes, logged_in, macnum; +extern char *line, *margv[]; +extern char *tcap_normal, *tcap_boldface; +extern char *tcap_underline, *tcap_reverse; +extern struct userinfo uinfo; +extern struct macel macros[]; +extern struct lslist *lshead, *lstail; +extern int is_ls; +#ifdef PASSIVEMODE +extern int passivemode; +#endif + +#ifdef GATEWAY +extern string gateway; +extern string gate_login; +#endif + + +#ifdef _POSIX_SOURCE +FILE *safeopen(int s, char *lmode){ + FILE *file; + + setreuid(geteuid(),getuid()); + setregid(getegid(),getgid()); + file=fdopen(s, lmode); + setreuid(geteuid(),getuid()); + setregid(getegid(),getgid()); + return(file); +} +#else +#define safeopen fdopen +#endif + + +int hookup(char *host, unsigned int port) +{ + register struct hostent *hp = 0; + int s, len, hErr = -1; + string errstr; + char **curaddr = NULL; + + bzero((char *)&hisctladdr, sizeof (hisctladdr)); +#ifdef BAD_INETADDR + hisctladdr.sin_addr = inet_addr(host); +#else + hisctladdr.sin_addr.s_addr = inet_addr(host); +#endif + if (hisctladdr.sin_addr.s_addr != -1) { + hisctladdr.sin_family = AF_INET; + (void) Strncpy(hostname, host); + } else { + hp = gethostbyname(host); + if (hp == NULL) { +#ifdef HERROR + extern int h_errno; + if (h_errno == HOST_NOT_FOUND) + (void) printf("%s: unknown host\n", host); + else (void) fprintf(stderr, "%s: gethostbyname herror (%d): ", + host, h_errno); + herror(NULL); +#else + (void) printf("%s: unknown host\n", host); +#endif + goto done; + } + hisctladdr.sin_family = hp->h_addrtype; + curaddr = hp->h_addr_list; + bcopy(*curaddr, (caddr_t)&hisctladdr.sin_addr, hp->h_length); + (void) Strncpy(hostname, hp->h_name); + } + s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + if (s < 0) { + PERROR("hookup", "socket"); + goto done; + } + hisctladdr.sin_port = port; +#ifdef SOCKS + while (Rconnect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) { +#else + while (Connect(s, &hisctladdr, sizeof (hisctladdr)) < 0) { +#endif + if (curaddr != NULL) { + curaddr++; + if (*curaddr != (char *)0) { + (void) sprintf(errstr, "connect error to address %s", + inet_ntoa(hisctladdr.sin_addr)); + PERROR("hookup", errstr); + bcopy(*curaddr, (caddr_t)&hisctladdr.sin_addr, hp->h_length); + dbprintf("Trying %s...\n", inet_ntoa(hisctladdr.sin_addr)); + (void) close(s); + s = socket(hisctladdr.sin_family, SOCK_STREAM, 0); + if (s < 0) { + PERROR("hookup", "socket"); + goto done; + } + continue; + } + } + PERROR("hookup", "connect"); + switch (errno) { + case ENETDOWN: + case ENETUNREACH: + case ECONNABORTED: + case ETIMEDOUT: + case ECONNREFUSED: + case EHOSTDOWN: + hErr = -2; /* we can re-try later. */ + } + goto bad; + } + len = sizeof (myctladdr); + if (Getsockname(s, (char *)&myctladdr, &len) < 0) { + PERROR("hookup", "getsockname"); + goto bad; + } + cin = safeopen(s, "r"); + cout = safeopen(dup(s), "w"); + if (cin == NULL || cout == NULL) { + (void) fprintf(stderr, "ftp: safeopen failed.\n"); + close_streams(0); + goto bad; + } + if (IS_VVERBOSE) + (void) printf("Connected to %s.\n", hostname); +#ifdef IPTOS_LOWDELAY /* control is interactive */ +#ifdef IP_TOS + { + int nType = IPTOS_LOWDELAY; + if (setsockopt(s, IPPROTO_IP, IP_TOS, + (char *) &nType, sizeof(nType)) < 0) { + PERROR("hookup", "setsockopt(IP_TOS)"); + } + } +#endif +#endif + if (getreply(0) > 2) { /* read startup message from server */ + close_streams(0); + if (code == 421) + hErr = -2; /* We can try again later. */ + goto bad; + } +#ifdef SO_OOBINLINE + { + int on = 1; + + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) + < 0 && debug) { + PERROR("hookup", "setsockopt(SO_OOBINLINE)"); + } + } +#endif /* SO_OOBINLINE */ + + hErr = 0; + goto done; + +bad: + (void) close(s); + if (cin != NULL) + (void) fclose(cin); + if (cout != NULL) + (void) fclose(cout); + cin = cout = NULL; +done: + return (hErr); +} /* hookup */ + + +/* This registers the user's username, password, and account with the remote + * host which validates it. If we get on, we also do some other things, like + * enter a log entry and execute the startup macro. + */ +int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit) +{ + string userName; + string str; + int n; + int sentAcct = 0; + int userWasPrompted = 0; + int result = CMDERR; + time_t now; + + if (userNamePtr == NULL) { + /* Prompt for a username. */ + (void) sprintf(str, "Login Name (%s): ", uinfo.username); + ++userWasPrompted; + if (Gets(str, userName, sizeof(userName)) == NULL) + goto done; + else if (userName[0]) { + /* User didn't just hit return. */ + userNamePtr = userName; + } else { + /* + * User can hit return if he wants to enter his username + * automatically. + */ + if (*uinfo.username != '\0') + userNamePtr = uinfo.username; + else + goto done; + } + } + +#ifdef GATEWAY + if (*gateway) + (void) sprintf(str, "USER %s@%s", + (*gate_login ? gate_login : dGATEWAY_LOGIN), + hostname); + else +#endif + (void) sprintf(str, "USER %s", userNamePtr); + + /* Send the user name. */ + n = command(str); + if (n == CONTINUE) { + if (passWordPtr == NULL) { + if (((strcmp("anonymous", userName) == 0) || + (strcmp("ftp", userName) == 0)) && (*anon_password != '\0')) + passWordPtr = anon_password; + else { + /* Prompt for a password. */ + ++userWasPrompted; + passWordPtr = Getpass("Password:"); + } + } + + /* The remote site is requesting us to send the password now. */ + (void) sprintf(str, "PASS %s", passWordPtr); + n = command(str); + if (n == CONTINUE) { + /* The remote site is requesting us to send the account now. */ + if (accountPtr == NULL) { + /* Prompt for a username. */ + (void) sprintf(str, "ACCT %s", Getpass("Account:")); + ++userWasPrompted; + } else { + (void) sprintf(str, "ACCT %s", accountPtr); + } + ++sentAcct; /* Keep track that we've sent the account already. */ + n = command(str); + } + } + + if (n != COMPLETE) { + (void) printf("Login failed.\n"); + goto done; + } + + /* If you specified an account, and the remote-host didn't request it + * (maybe it's optional), we will send the account information. + */ + if (!sentAcct && accountPtr != NULL) { + (void) sprintf(str, "ACCT %s", accountPtr); + (void) command(str); + } + + /* See if remote host dropped connection. Some sites will let you log + * in anonymously, only to tell you that they already have too many + * anon users, then drop you. We do a no-op here to see if they've + * ditched us. + */ + n = quiet_command("NOOP"); + if (n == TRANSIENT) + goto done; + +#ifdef SYSLOG + syslog(LOG_INFO, "%s connected to %s as %s.", + uinfo.username, hostname, userNamePtr); +#endif + + /* Save which sites we opened to the user's logfile. */ + if (logf != NULL) { + (void) time(&now); + (void) fprintf(logf, "%s opened at %s", + hostname, + ctime(&now)); + } + + /* Let the user know we are logged in, unless he was prompted for some + * information already. + */ + if (!userWasPrompted) + if (NOT_VQUIET) + (void) printf("Logged into %s.\n", hostname); + + if ((doInit) && (macnum > 0)) { + /* Run the startup macro, if any. */ + /* If macnum is non-zero, the init macro was defined from + * ruserpass. It would be the only macro defined at this + * point. + */ + (void) strcpy(line, "$init"); + makeargv(); + (void) domacro(margc, margv); + } + + _cd(NULL); /* Init cwd variable. */ + + result = NOERR; + logged_in = 1; + +done: + return (result); +} /* Login */ + + + +/*ARGSUSED*/ +void cmdabort SIG_PARAMS +{ + (void) printf("\n"); + (void) fflush(stdout); + abrtflag++; +} /* cmdabort */ + + + + +int CommandWithFlags(char *cmd, int flags) +{ + int r; + Sig_t oldintr; + string str; + + if (cmd == NULL) { + /* Should never happen; bug if it does. */ + PERROR("command", "NULL command"); + return (-1); + } + abrtflag = 0; + if (debug) { + if (strncmp(cmd, "PASS", (size_t)4) == 0) + dbprintf("cmd: \"PASS ********\"\n"); + else + dbprintf("cmd: \"%s\" (length %d)\n", cmd, (int) strlen(cmd)); + } + if (cout == NULL) { + (void) sprintf(str, "%s: No control connection for command", cmd); + PERROR("command", str); + return (0); + } + oldintr = Signal(SIGINT, /* cmdabort */ SIG_IGN); + + /* Used to have BROKEN_MEMCPY tested here. */ + if (cout != NULL) + (void) fprintf(cout, "%s\r\n", cmd); + + (void) fflush(cout); + cpend = 1; + r = (flags == WAIT_FOR_REPLY) ? + (getreply(strcmp(cmd, "QUIT") == 0)) : PRELIM; + if (abrtflag && oldintr != SIG_IGN && oldintr != NULL) + (*oldintr)(0); + (void) Signal(SIGINT, oldintr); + return(r); +} /* CommandWithFlags */ + + + +/* This stub runs 'CommandWithFlags' above, telling it to wait for + * reply after the command is sent. + */ +int command(char *cmd) +{ + return (CommandWithFlags(cmd, WAIT_FOR_REPLY)); +} /* command */ + +/* This stub runs 'CommandWithFlags' above, telling it to NOT wait for + * reply after the command is sent. + */ +int command_noreply(char *cmd) +{ + return(CommandWithFlags(cmd, DONT_WAIT_FOR_REPLY)); +} /* command */ + + + +int quiet_command(char *cmd) +{ + register int oldverbose, result; + + oldverbose = verbose; + verbose = debug ? V_VERBOSE : V_QUIET; + result = command(cmd); + verbose = oldverbose; + return (result); +} /* quiet_command */ + + + + +int verbose_command(char *cmd) +{ + register int oldverbose, result; + + oldverbose = verbose; + verbose = V_VERBOSE; + result = command(cmd); + verbose = oldverbose; + return (result); +} /* quiet_command */ + + + + +int getreply(int expecteof) +{ + register int c, n = 0; + int dig; + char *cp, *end, *dp; + int thiscode, originalcode = 0, continuation = 0; + Sig_t oldintr; + + if (cin == NULL) + return (-1); + /* oldintr = Signal(SIGINT, SIG_IGN); */ + oldintr = Signal(SIGINT, cmdabort); + end = reply_string + RECEIVEDLINELEN - 2; + for (;abrtflag==0;) { + dig = n = thiscode = code = 0; + cp = reply_string; + for (;abrtflag==0;) { + c = fgetc(cin); + if (c == IAC) { /* handle telnet commands */ + switch (c = fgetc(cin)) { + case WILL: + case WONT: + c = fgetc(cin); + (void) fprintf(cout, "%c%c%c",IAC,DONT,c); + (void) fflush(cout); + break; + case DO: + case DONT: + c = fgetc(cin); + (void) fprintf(cout, "%c%c%c",IAC,WONT,c); + (void) fflush(cout); + break; + default: + break; + } + continue; + } + dig++; + if (c == EOF) { + if (expecteof) { + (void) Signal(SIGINT, oldintr); + code = 221; + return (0); + } + lostpeer(0); + if (NOT_VQUIET) { + (void) printf("421 Service not available, remote server has closed connection\n"); + (void) fflush(stdout); + } + code = 421; + return(4); + } + if (cp < end && c != '\r') + *cp++ = c; + + if (c == '\n') + break; + if (dig < 4 && isdigit(c)) + code = thiscode = code * 10 + (c - '0'); + else if (dig == 4 && c == '-') { + if (continuation) + code = 0; + continuation++; + } + if (n == 0) + n = c; + } /* end for(;;) #2 */ + + *cp = '\0'; + switch (verbose) { + case V_QUIET: + /* Don't print anything. */ + break; + case V_ERRS: + if (n == '5') { + dp = reply_string; + goto stripCode; + } + break; + case V_IMPLICITCD: + case V_TERSE: + dp = NULL; + if (n == '5' && verbose == V_TERSE) + dp = reply_string; + else { + switch (thiscode) { + case 230: + case 214: + case 331: + case 332: + case 421: /* For ftp.apple.com, etc. */ + dp = reply_string; + break; + case 220: + /* + * Skip the foo FTP server ready line. + */ + if (strstr(reply_string, "ready.") == NULL) + dp = reply_string; + break; + case 250: + /* + * Print 250 lines if they aren't + * "250 CWD command successful." + */ + if (strncmp(reply_string + 4, "CWD ", (size_t) 4)) + dp = reply_string; + } + } + if (dp == NULL) break; +stripCode: + /* Try to strip out the code numbers, etc. */ + if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) { + if (*dp == ' ' || *dp == '-') { + dp++; + if (*dp == ' ') dp++; + } else dp = reply_string; + } else { + int spaces; + dp = reply_string; + for (spaces = 0; spaces < 4; ++spaces) + if (dp[spaces] != ' ') + break; + if (spaces == 4) + dp += spaces; + } + goto printLine; + case V_VERBOSE: + dp = reply_string; +printLine: (void) fputs(dp, stdout); + } /* end switch */ + + if (continuation && code != originalcode) { + if (originalcode == 0) + originalcode = code; + continue; + } + if (n != '1') + cpend = 0; + (void) Signal(SIGINT,oldintr); + if (code == 421 || originalcode == 421) + lostpeer(0); + if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr) + (*oldintr)(0); + break; + } /* end for(;;) #1 */ + return (n - '0'); +} /* getreply */ + + + + +static int empty(struct fd_set *mask, int sec) +{ + struct timeval t; + + t.tv_sec = (long) sec; + t.tv_usec = 0; + + return(Select(32, mask, NULL, NULL, &t)); +} /* empty */ + + + + +static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0) +{ + tdiff->tv_sec = t1->tv_sec - t0->tv_sec; + tdiff->tv_usec = t1->tv_usec - t0->tv_usec; + if (tdiff->tv_usec < 0) + tdiff->tv_sec--, tdiff->tv_usec += 1000000; +} /* tvsub */ + + +/* Variables private to progress_report code. */ +static int barlen; +static long last_dot; +static int dots; + +int start_progress(int sending, char *local) +{ + long s; + char spec[64]; + + cur_progress_meter = toatty ? progress_meter : 0; + if ((cur_progress_meter > pr_last) || (cur_progress_meter < 0)) + cur_progress_meter = dPROGRESS; + if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar) || (cur_progress_meter == pr_last))) + cur_progress_meter = pr_kbytes; + if (!ansi_escapes && (cur_progress_meter == pr_philbar)) + cur_progress_meter = pr_dots; + + (void) Gettimeofday(&start); + now_sec = start.tv_sec; + + switch (cur_progress_meter) { + case pr_none: + break; + case pr_percent: + (void) printf("%s: ", local); + goto zz; + case pr_kbytes: + (void) printf("%s: ", local); + goto zz; + case pr_philbar: + (void) printf("%s%s file: %s %s\n", + tcap_boldface, + sending ? "Sending" : "Receiving", + local, + tcap_normal + ); + barlen = 52; + for (s = file_size; s > 0; s /= 10L) barlen--; + (void) sprintf(spec, " 0 %%%ds %%ld bytes. ETA: --:--\r", + barlen); + (void) printf(spec, " ", file_size); + goto zz; + case pr_dots: + last_dot = (file_size / 10) + 1; + dots = 0; + (void) printf("%s: ", local); + zz: + (void) fflush(stdout); + Echo(stdin, 0); + } /* end switch */ + return (cur_progress_meter); +} /* start_progress */ + + + + +int progress_report(int finish_up) +{ + int size; + int perc; + float frac; + char spec[64]; + float secsElap; + int secsLeft, minLeft; + struct timeval td; + + next_report += xferbufsize; + (void) Gettimeofday(&stop); + if ((stop.tv_sec > now_sec) || (finish_up && file_size)) { + switch (cur_progress_meter) { + case pr_none: + break; + case pr_percent: + perc = (int) (100.0 * (float)bytes / (float)file_size); + if (perc > 100) perc = 100; + else if (perc < 0) perc = 0; + (void) printf("\b\b\b\b%3d%%", perc); + (void) fflush(stdout); + break; + case pr_philbar: + frac = (float)bytes / (float)file_size; + if (frac > 1.0) + frac = 1.0; + else if (frac < 0.0) + frac = 0.0; + size = (int) ((float)barlen * frac); + (void) sprintf(spec, + "%%3d%%%% 0 %%s%%%ds%%s%%%ds %%ld bytes. ETA:%%3d:%%02d\r", + size, barlen - size); + perc = (long) (100.0 * frac); + tvsub(&td, &stop, &start); + secsElap = td.tv_sec + (td.tv_usec / 1000000.0); + secsLeft = (int) ((float)file_size / ((float)bytes/secsElap) - + secsElap + 0.5); + minLeft = secsLeft / 60; + secsLeft = secsLeft - (minLeft * 60); + (void) printf( + spec, + perc, + tcap_reverse, + "", + tcap_normal, + "", + file_size, + minLeft, + secsLeft + ); + (void) fflush(stdout); + break; + case pr_kbytes: + if ((bytes / 1024) > 0) { + (void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024); + (void) fflush(stdout); + } + break; + case pr_dots: + if (bytes > last_dot) { + (void) fputc('.', stdout); + (void) fflush(stdout); + last_dot += (file_size / 10) + 1; + dots++; + } + } /* end switch */ + now_sec = stop.tv_sec; + } /* end if we updated */ + return (UserLoggedIn()); +} /* progress_report */ + + + + + +void end_progress(char *direction, char *local, char *remote) +{ + struct timeval td; + float s, bs = 0.0; + str32 bsstr; + int doLastReport; + int receiving; + longstring fullRemote, fullLocal; + + doLastReport = ((UserLoggedIn()) && (cur_progress_meter != pr_none) && + (NOT_VQUIET) && (bytes > 0)); + + receiving = (direction[0] == 'r'); + + switch(FileType(local)) { + case IS_FILE: + (void) Strncpy(fullLocal, lcwd); + (void) Strncat(fullLocal, "/"); + (void) Strncat(fullLocal, local); + break; + case IS_PIPE: + doLastReport = 0; + local = Strncpy(fullLocal, local); + break; + case IS_STREAM: + default: + doLastReport = 0; + local = Strncpy(fullLocal, receiving ? "stdout" : "stdin"); + } + + if (doLastReport) + (void) progress_report(1); /* tell progress proc to cleanup. */ + + tvsub(&td, &stop, &start); + s = td.tv_sec + (td.tv_usec / 1000000.0); + + bsstr[0] = '\0'; + if (s != 0.0) { + bs = (float)bytes / s; + if (bs > 1024.0) + sprintf(bsstr, "%.2f K/s", bs / 1024.0); + else + sprintf(bsstr, "%.2f Bytes/sec", bs); + } + + if (doLastReport) switch(cur_progress_meter) { + case pr_none: + zz: + (void) printf("%s: %ld bytes %s in %.2f seconds, %s.\n", + local, bytes, direction, s, bsstr); + break; + case pr_kbytes: + case pr_percent: + (void) printf("%s%ld bytes %s in %.2f seconds, %s.\n", + cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b", + bytes, direction, s, bsstr); + Echo(stdin, 1); + break; + case pr_philbar: + (void) printf("\n"); + Echo(stdin, 1); + goto zz; + case pr_dots: + for (; dots < 10; dots++) + (void) fputc('.', stdout); + (void) fputc('\n', stdout); + Echo(stdin, 1); + goto zz; + } + + /* Save transfers to the logfile. */ + /* if a simple path is given, try to log the full path */ + if (*remote != '/') { + (void) Strncpy(fullRemote, cwd); + (void) Strncat(fullRemote, "/"); + (void) Strncat(fullRemote, remote); + } else + (void) Strncpy(fullRemote, remote); + + if (logf != NULL) { + (void) fprintf(logf, "\t-> \"%s\" %s, %s\n", + fullRemote, direction, bsstr); + } +#ifdef SYSLOG + { + longstring infoPart1; + + /* Some syslog()'s can't take an unlimited number of arguments, + * so shorten our call to syslog to 5 arguments total. + */ + Strncpy(infoPart1, uinfo.username); + if (receiving) { + Strncat(infoPart1, " received "); + Strncat(infoPart1, fullRemote); + Strncat(infoPart1, " as "); + Strncat(infoPart1, fullLocal); + Strncat(infoPart1, " from "); + } else { + Strncat(infoPart1, " sent "); + Strncat(infoPart1, fullLocal); + Strncat(infoPart1, " as "); + Strncat(infoPart1, fullRemote); + Strncat(infoPart1, " to "); + } + Strncat(infoPart1, hostname); + syslog (LOG_INFO, "%s (%ld bytes, %s).", infoPart1, bytes, bsstr); + } +#endif /* SYSLOG */ +} /* end_progress */ + + + + +void close_file(FILE **fin, int filetype) +{ + if (*fin != NULL) { + if (filetype == IS_FILE) { + (void) fclose(*fin); + *fin = NULL; + } else if (filetype == IS_PIPE) { + (void) pclose(*fin); + *fin = NULL; + } + } +} /* close_file */ + + + + +/*ARGSUSED*/ +void abortsend SIG_PARAMS +{ + activemcmd = 0; + abrtflag = 0; + (void) fprintf(stderr, "\nSend aborted.\n"); + Echo(stdin, 1); + longjmp(sendabort, 1); +} /* abortsend */ + + + +int sendrequest(char *cmd, char *local, char *remote) +{ + FILE *fin, *dout = NULL; + Sig_t oldintr, oldintp; + string str; + register int c, d; + struct stat st; + int filetype, result = NOERR; + int do_reports = 0; + char *mode; + register char *bufp; + + dbprintf("cmd: %s; rmt: %s; loc: %s.\n", + cmd, + remote == NULL ? "(null)" : remote, + local == NULL ? "(null)" : local + ); + + oldintr = NULL; + oldintp = NULL; + mode = "w"; + bytes = file_size = 0L; + if (setjmp(sendabort)) { + while (cpend) { + (void) getreply(0); + } + if (data >= 0) { + (void) close(data); + data = -1; + } + if (oldintr) + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + result = -1; + goto xx; + } + oldintr = Signal(SIGINT, abortsend); + file_size = -1; + if (strcmp(local, "-") == 0) { + fin = stdin; + filetype = IS_STREAM; + } else if (*local == '|') { + filetype = IS_PIPE; + oldintp = Signal(SIGPIPE,SIG_IGN); + fin = popen(local + 1, "r"); + if (fin == NULL) { + PERROR("sendrequest", local + 1); + (void) Signal(SIGINT, oldintr); + (void) Signal(SIGPIPE, oldintp); + result = -1; + goto xx; + } + } else { + filetype = IS_FILE; + fin = fopen(local, "r"); + if (fin == NULL) { + PERROR("sendrequest", local); + (void) Signal(SIGINT, oldintr); + result = -1; + goto xx; + } + if (fstat(fileno(fin), &st) < 0 || + (st.st_mode&S_IFMT) != S_IFREG) { + (void) fprintf(stdout, "%s: not a plain file.\n", local); + (void) Signal(SIGINT, oldintr); + (void) fclose(fin); + result = -1; + goto xx; + } + file_size = st.st_size; + } + if (initconn()) { + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + result = -1; + close_file(&fin, filetype); + goto xx; + } + if (setjmp(sendabort)) + goto Abort; + +#ifdef TRY_NOREPLY + if (remote) { + (void) sprintf(str, "%s %s", cmd, remote); + (void) command_noreply(str); + } else { + (void) command_noreply(cmd); + } + + dout = dataconn(mode); + if (dout == NULL) + goto Abort; + + if(getreply(0) != PRELIM) { + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + close_file(&fin, filetype); + return -1; + } +#else + if (remote) { + (void) sprintf(str, "%s %s", cmd, remote); + if (command(str) != PRELIM) { + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + close_file(&fin, filetype); + goto xx; + } + } else { + if (command(cmd) != PRELIM) { + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + close_file(&fin, filetype); + goto xx; + } + } + + dout = dataconn(mode); + if (dout == NULL) + goto Abort; +#endif + + (void) Gettimeofday(&start); + oldintp = Signal(SIGPIPE, SIG_IGN); + if ((do_reports = (filetype == IS_FILE && NOT_VQUIET)) != 0) + do_reports = start_progress(1, local); + + switch (curtype) { + + case TYPE_I: + case TYPE_L: + errno = d = 0; + while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) { + bytes += c; + for (bufp = xferbuf; c > 0; c -= d, bufp += d) + if ((d = write(fileno(dout), bufp, c)) <= 0) + break; + /* Print progress indicator. */ + if (do_reports) + do_reports = progress_report(0); + } + if (c < 0) + PERROR("sendrequest", local); + if (d <= 0) { + if (d == 0 && !creating) + (void) fprintf(stderr, "netout: write returned 0?\n"); + else if (errno != EPIPE) + PERROR("sendrequest", "netout"); + bytes = -1; + } + break; + + case TYPE_A: + next_report = xferbufsize; + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + if (ferror(dout)) + break; + (void) putc('\r', dout); + bytes++; + } + (void) putc(c, dout); + bytes++; + + /* Print progress indicator. */ + if (do_reports && bytes > next_report) + do_reports = progress_report(0); + } + if (ferror(fin)) + PERROR("sendrequest", local); + if (ferror(dout)) { + if (errno != EPIPE) + PERROR("sendrequest", "netout"); + bytes = -1; + } + break; + } +Done: + close_file(&fin, filetype); + if (dout) + (void) fclose(dout); + (void) getreply(0); + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + end_progress("sent", local, remote); +xx: + return (result); +Abort: + result = -1; + if (!cpend) + goto xx; + if (data >= 0) { + (void) close(data); + data = -1; + } + goto Done; +} /* sendrequest */ + + + + +/*ARGSUSED*/ +void abortrecv SIG_PARAMS +{ + activemcmd = 0; + abrtflag = 0; + (void) fprintf(stderr, +#ifdef TRY_ABOR + "(abort)\n"); +#else + "\nAborting, please wait..."); +#endif + (void) fflush(stderr); + Echo(stdin, 1); + longjmp(recvabort, 1); +} /* abortrecv */ + + + + +void GetLSRemoteDir(char *remote, char *remote_dir) +{ + char *cp; + + /* + * The ls() function can specify a directory to list along with ls flags, + * if it sends the flags first followed by the directory name. + * + * So far, we don't care about the remote directory being listed. I put + * it now so I won't forget in case I need to do something with it later. + */ + remote_dir[0] = 0; + if (remote != NULL) { + cp = index(remote, LS_FLAGS_AND_FILE); + if (cp == NULL) + (void) Strncpy(remote_dir, remote); + else { + *cp++ = ' '; + (void) Strncpy(remote_dir, cp); + } + } +} /* GetLSRemoteDir */ + + + + +int AdjustLocalFileName(char *local) +{ + char *dir; + + /* See if the file exists, and if we can overwrite it. */ + if ((access(local, 0) == 0) && (access(local, 2) < 0)) + goto noaccess; + + /* + * Make sure we are writing to a valid local path. + * First check the local directory, and see if we can write to it. + */ + if (access(local, 2) < 0) { + dir = rindex(local, '/'); + + if (errno != ENOENT && errno != EACCES) { + /* Report an error if it's one we can't handle. */ + PERROR("AdjustLocalFileName", local); + return -1; + } + /* See if we have write permission on this directory. */ + if (dir != NULL) { + /* Special case: /filename. */ + if (dir != local) + *dir = 0; + if (access(dir == local ? "/" : local, 2) < 0) { + /* + * We have a big long pathname, like /a/b/c/d, + * but see if we can write into the current + * directory and call the file ./d. + */ + if (access(".", 2) < 0) { + (void) strcpy(local, " and ."); + goto noaccess; + } + (void) strcpy(local, dir + 1); /* use simple filename. */ + } else + *dir = '/'; + } else { + /* We have a simple path name (file name only). */ + if (access(".", 2) < 0) { +noaccess: PERROR("AdjustLocalFileName", local); + return -1; + } + } + } + return (NOERR); +} /* AdjustLocalFileName */ + + + +int SetToAsciiForLS(int is_retr, int currenttype) +{ + int oldt = -1, oldv; + + if (!is_retr) { + if (currenttype != TYPE_A) { + oldt = currenttype; + oldv = verbose; + if (!debug) + verbose = V_QUIET; + (void) setascii(0, NULL); + verbose = oldv; + } + } + return oldt; +} /* SetToAsciiForLS */ + + + +int IssueCommand(char *ftpcmd, char *remote) +{ + string str; + int result = NOERR; + + if (remote) + (void) sprintf(str, "%s %s", ftpcmd, remote); + else + (void) Strncpy(str, ftpcmd); + +#ifdef TRY_NOREPLY + if (command_noreply(str) != PRELIM) +#else + if (command(str) != PRELIM) +#endif + result = -1; + return (result); +} /* IssueCommand */ + + + +FILE *OpenOutputFile(int filetype, char *local, char *mode, Sig_t *oldintp) +{ + FILE *fout; + + if (filetype == IS_STREAM) { + fout = stdout; + } else if (filetype == IS_PIPE) { + /* If it is a pipe, the pipecmd will have a | as the first char. */ + ++local; + fout = popen(local, "w"); + *oldintp = Signal(SIGPIPE, abortrecv); + } else { + fout = fopen(local, mode); + } + if (fout == NULL) + PERROR("OpenOutputFile", local); + return (fout); +} /* OpenOutputFile */ + + + +void ReceiveBinary(FILE *din, FILE *fout, int *do_reports, char *localfn) +{ + int c, d, do2; + + errno = 0; /* Clear any old error left around. */ + do2 = *do_reports; /* A slight optimization :-) */ + bytes = 0; /* Init the byte-transfer counter. */ + + for (;;) { + /* Read a block from the input stream. */ + c = read(fileno(din), xferbuf, (int)xferbufsize); + + /* If c is zero, then we've read the whole file. */ + if (c == 0) + break; + + /* Check for errors that may have occurred while reading. */ + if (c < 0) { + /* Error occurred while reading. */ + if (errno != EPIPE) + PERROR("ReceiveBinary", "netin"); + bytes = -1; + break; + } + + /* Write out the same block we just read in. */ + d = write(fileno(fout), xferbuf, c); + + /* Check for write errors. */ + if ((d < 0) || (ferror(fout))) { + /* Error occurred while writing. */ + PERROR("ReceiveBinary", "outfile"); + break; + } + if (d < c) { + (void) fprintf(stderr, "%s: short write\n", localfn); + break; + } + + /* Update the byte counter. */ + bytes += (long) c; + + /* Print progress indicator. */ + if (do2 != 0) + do2 = progress_report(0); + } + + *do_reports = do2; /* Update the real do_reports variable. */ +} /* ReceiveBinary */ + + + +void AddRedirLine(char *str2) +{ + register struct lslist *new; + + (void) Strncpy(indataline, str2); + new = (struct lslist *) malloc((size_t) sizeof(struct lslist)); + if (new != NULL) { + if ((new->string = NewString(str2)) != NULL) { + new->next = NULL; + if (lshead == NULL) + lshead = lstail = new; + else { + lstail->next = new; + lstail = new; + } + } + } +} /* AddRedirLine */ + + + +void ReceiveAscii(FILE *din, FILE *fout, int *do_reports, char *localfn, int +lineMode) +{ + string str2; + int nchars = 0, c; + char *linePtr; + int do2 = *do_reports, stripped; + + next_report = xferbufsize; + bytes = errno = 0; + if (lineMode) { + while ((linePtr = FGets(str2, din)) != NULL) { + bytes += (long) RemoveTrailingNewline(linePtr, &stripped); + if (is_ls || debug > 0) + AddRedirLine(linePtr); + + /* Shutup while getting remote size and mod time. */ + if (!buffer_only) { + c = fputs(linePtr, fout); + + if (c != EOF) { + if (stripped > 0) + c = fputc('\n', fout); + } + if ((c == EOF) || (ferror(fout))) { + PERROR("ReceiveAscii", "outfile"); + break; + } + } + + /* Print progress indicator. */ + if (do2 && bytes > next_report) + do2 = progress_report(0); + } + } else while ((c = getc(din)) != EOF) { + linePtr = str2; + while (c == '\r') { + bytes++; + if ((c = getc(din)) != '\n') { + if (ferror(fout)) + goto break2; + /* Shutup while getting remote size and mod time. */ + if (!buffer_only) + (void) putc('\r', fout); + if (c == '\0') { + bytes++; + goto contin2; + } + if (c == EOF) + goto contin2; + } + } + /* Shutup while getting remote size and mod time. */ + if (!buffer_only) + (void) putc(c, fout); + bytes++; + + /* Print progress indicator. */ + if (do2 && bytes > next_report) + do2 = progress_report(0); + + /* No seg violations, please */ + if (nchars < sizeof(str2) - 1) { + *linePtr++ = c; /* build redir string */ + nchars++; + } + + contin2: + /* Save the input line in the buffer for recall later. */ + if (c == '\n' && is_ls) { + *--linePtr = 0; + AddRedirLine(str2); + nchars = 0; + } + + } /* while ((c = getc(din)) != EOF) */ +break2: + if (ferror(din)) { + if (errno != EPIPE) + PERROR("ReceiveAscii", "netin"); + bytes = -1; + } + if (ferror(fout)) { + if (errno != EPIPE) + PERROR("ReceiveAscii", localfn); + } + *do_reports = do2; +} /* ReceiveAscii */ + + + +void CloseOutputFile(FILE *f, int filetype, char *name, time_t mt) +{ + struct utimbuf ut; + + if (f != NULL) { + (void) fflush(f); + if (filetype == IS_FILE) { + (void) fclose(f); +#ifndef DONT_TIMESTAMP + if (mt != (time_t)0) { + ut.actime = ut.modtime = mt; + (void) utime(name, &ut); + } +#endif /* DONT_TIMESTAMP */ + } else if (filetype == IS_PIPE) { + (void)pclose(f); + } + } +} /* close_file */ + + + +void ResetOldType(int oldtype) +{ + int oldv; + + if (oldtype >= 0) { + oldv = verbose; + if (!debug) + verbose = V_QUIET; + (void) SetTypeByNumber(oldtype); + verbose = oldv; + } +} /* ResetOldType */ + + + +int FileType(char *fname) +{ + int ft = IS_FILE; + + if (strcmp(fname, "-") == 0) + ft = IS_STREAM; + else if (*fname == '|') + ft = IS_PIPE; + return (ft); +} /* FileType */ + + + + +void CloseData(void) { + if (data >= 0) { + (void) close(data); + data = -1; + } +} /* CloseData */ + + + + +int recvrequest(char *cmd, char *local, char *remote, char *mode) +{ + FILE *fout = NULL, *din = NULL; + Sig_t oldintr = NULL, oldintp = NULL; + int oldtype = -1, is_retr; + int nfnd; + char msg; + struct fd_set mask; + int filetype, do_reports = 0; + string remote_dir; + time_t remfTime = 0; + int result = -1; + + dbprintf("---> cmd: %s; rmt: %s; loc: %s; mode: %s.\n", + cmd, + remote == NULL ? "(null)" : remote, + local == NULL ? "(null)" : local, + mode + ); + + is_retr = strcmp(cmd, "RETR") == 0; + + GetLSRemoteDir(remote, remote_dir); + if ((filetype = FileType(local)) == IS_FILE) { + if (AdjustLocalFileName(local)) + goto xx; + } + + file_size = -1; + if (filetype == IS_FILE) + file_size = GetDateAndSize(remote, (unsigned long *) &remfTime); + + if (initconn()) + goto xx; + + oldtype = SetToAsciiForLS(is_retr, curtype); + + /* Issue the NLST command but don't wait for the reply. Some FTP + * servers make the data connection before issuing the + * "150 Opening ASCII mode data connection for /bin/ls" reply. + */ + if (IssueCommand(cmd, remote)) + goto xx; + + if ((fout = OpenOutputFile(filetype, local, mode, &oldintp)) == NULL) + goto xx; + + if ((din = dataconn("r")) == NULL) + goto Abort; + +#ifdef TRY_NOREPLY + /* Now get the reply we skipped above. */ + (void) getreply(0); +#endif + + do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE; + if (do_reports) + do_reports = start_progress(0, local); + + if (setjmp(recvabort)) { +#ifdef TRY_ABOR + goto Abort; +#else + /* Just read the rest of the stream without doing anything with + * the results. + */ + (void) Signal(SIGINT, SIG_IGN); + (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */ + while (read(fileno(din), xferbuf, (int)xferbufsize) > 0) + ; + (void) fprintf(stderr, "\rAborted. \n"); +#endif + } else { + oldintr = Signal(SIGINT, abortrecv); + + if (curtype == TYPE_A) + ReceiveAscii(din, fout, &do_reports, local, 1); + else + ReceiveBinary(din, fout, &do_reports, local); + result = NOERR; + /* Don't interrupt us now, since we finished successfully. */ + (void) Signal(SIGPIPE, SIG_IGN); + (void) Signal(SIGINT, SIG_IGN); + } + CloseData(); + (void) getreply(0); + + goto xx; + +Abort: + +/* Abort using RFC959 recommended IP,SYNC sequence */ + + (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */ + (void) Signal(SIGINT, SIG_IGN); + if (!cpend || !cout) goto xx; + (void) fprintf(cout,"%c%c",IAC,IP); + (void) fflush(cout); + msg = IAC; +/* send IAC in urgent mode instead of DM because UNIX places oob mark */ +/* after urgent byte rather than before as now is protocol */ + if (send(fileno(cout),&msg,1,MSG_OOB) != 1) + PERROR("recvrequest", "abort"); + (void) fprintf(cout,"%cABOR\r\n",DM); + (void) fflush(cout); + FD_ZERO(&mask); + FD_SET(fileno(cin), &mask); + if (din) + FD_SET(fileno(din), &mask); + if ((nfnd = empty(&mask,10)) <= 0) { + if (nfnd < 0) + PERROR("recvrequest", "abort"); + lostpeer(0); + } + if (din && FD_ISSET(fileno(din), &mask)) { + while ((read(fileno(din), xferbuf, xferbufsize)) > 0) + ; + } + if ((getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */ + CloseData(); + (void) getreply(0); + } + (void) getreply(0); + result = -1; + CloseData(); + +xx: + CloseOutputFile(fout, filetype, local, remfTime); + dbprintf("outfile closed.\n"); + if (din) + (void) fclose(din); + if (is_retr) + end_progress("received", local, remote); + if (oldintr) + (void) Signal(SIGINT, oldintr); + if (oldintp) + (void) Signal(SIGPIPE, oldintp); + dbprintf("recvrequest result = %d.\n", result); + if (oldtype >= 0) + ResetOldType(oldtype); + bytes = 0L; + return (result); +} /* recvrequest */ + + + + +/* + * Need to start a listen on the data channel + * before we send the command, otherwise the + * server's connect may fail. + */ + + +int initconn(void) +{ + register char *p, *a; + int result, len, tmpno = 0; + int on = 1, rval; + string str; + Sig_t oldintr; +#ifdef PASSIVEMODE + int a1, a2, a3, a4, p1, p2; + unsigned char n[6]; +#endif + + oldintr = Signal(SIGINT, SIG_IGN); + +#ifdef PASSIVEMODE + if (passivemode) { + data = socket(AF_INET, SOCK_STREAM, 0); + if (data < 0) { + PERROR("initconn", "socket"); + rval = 1; + goto Return; + } + if (options & SO_DEBUG && + setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0 ) { + PERROR("initconn", "setscokopt (ignored)"); + } + result = command("PASV"); + if (result != COMPLETE) { + printf("Passive mode refused.\n"); + rval = 1; + goto Return; + } + /* + * What we've got here is a string of comma separated one-byte + * unsigned integer values. The first four are the IP address, + * the fifth is the MSB of the port address, and the sixth is the + * LSB of the port address. Extract this data and prepare a + * 'data_addr' (struct sockaddr_in). + */ + if (sscanf(reply_string+27, "%d,%d,%d,%d,%d,%d", + &a1, &a2, &a3, &a4, &p1, &p2) != 6) { + printf("Cannot parse PASV response: %s\n", reply_string); + rval = 1; + goto Return; + } + n[0] = (unsigned char) a1; + n[1] = (unsigned char) a2; + n[2] = (unsigned char) a3; + n[3] = (unsigned char) a4; + n[4] = (unsigned char) p1; + n[5] = (unsigned char) p2; + + data_addr.sin_family = AF_INET; + bcopy( (void *)&n[0], (void *)&data_addr.sin_addr, 4 ); + bcopy( (void *)&n[4], (void *)&data_addr.sin_port, 2 ); + + if (Connect( data, &data_addr, sizeof(data_addr) ) < 0 ) { + PERROR("initconn", "connect"); + rval = 1; + goto Return; + } + rval = 0; + goto Return; + } +#endif + +noport: + data_addr = myctladdr; + if (sendport) + data_addr.sin_port = 0; /* let system pick one */ + if (data != -1) + (void) close (data); + data = socket(AF_INET, SOCK_STREAM, 0); + if (data < 0) { + PERROR("initconn", "socket"); + if (tmpno) + sendport = 1; + rval = 1; goto Return; + } + if (!sendport) + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) { + PERROR("initconn", "setsockopt (reuse address)"); + goto bad; + } +#ifdef SOCKS + if (Rbind(data, (struct sockaddr *)&data_addr, sizeof (data_addr), hisctladdr.sin_addr.s_addr) < 0) { +#else + if (Bind(data, &data_addr, sizeof (data_addr)) < 0) { +#endif + PERROR("initconn", "bind"); + goto bad; + } +#ifdef LINGER /* If puts don't complete, you could try this. */ + { + struct linger li; + li.l_onoff = 1; + li.l_linger = 900; + + if (setsockopt(data, SOL_SOCKET, SO_LINGER, + (char *)&li, sizeof(struct linger)) < 0) + { + PERROR("initconn", "setsockopt(SO_LINGER)"); + } + } +#endif /* LINGER */ + +#ifdef IPTOS_THROUGHPUT /* transfers are background */ +#ifdef IP_TOS + { + int nType = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, + (char *) &nType, sizeof(nType)) < 0) { + PERROR("initconn", "setsockopt(IP_TOS)"); + } + } +#endif +#endif + + if (options & SO_DEBUG && + setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) + PERROR("initconn", "setsockopt (ignored)"); + len = sizeof (data_addr); + if (Getsockname(data, (char *)&data_addr, &len) < 0) { + PERROR("initconn", "getsockname"); + goto bad; + } + +#ifdef SOCKS + if (Rlisten(data, 1) < 0) +#else + if (listen(data, 1) < 0) +#endif + PERROR("initconn", "listen"); + if (sendport) { + a = (char *)&data_addr.sin_addr; + p = (char *)&data_addr.sin_port; +#define UC(x) (int) (((int) x) & 0xff) + (void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + result = command(str); + if (result == ERROR && sendport == -1) { + sendport = 0; + tmpno = 1; + goto noport; + } + rval = (result != COMPLETE); goto Return; + } + if (tmpno) + sendport = 1; + rval = 0; goto Return; +bad: + (void) close(data), data = -1; + if (tmpno) + sendport = 1; + rval = 1; +Return: + (void) Signal(SIGINT, oldintr); + return (rval); +} /* initconn */ + + + + +FILE * +dataconn(char *mode) +{ + struct sockaddr_in from; + FILE *fp; + int s, fromlen = sizeof (from); + +#ifdef SOCKS + s = Raccept(data, (struct sockaddr *) &from, &fromlen); +#else +#ifdef PASSIVEMODE + if (passivemode) + return( fdopen( data, mode )); +#endif + s = Accept(data, &from, &fromlen); +#endif + if (s < 0) { + PERROR("dataconn", "accept"); + (void) close(data), data = -1; + fp = NULL; + } else { + (void) close(data); + data = s; + fp = safeopen(data, mode); + } + return (fp); +} /* dataconn */ + +/* eof ftp.c */ diff --git a/usr.bin/ncftp/ftp.h b/usr.bin/ncftp/ftp.h new file mode 100644 index 000000000000..d531723f257f --- /dev/null +++ b/usr.bin/ncftp/ftp.h @@ -0,0 +1,67 @@ +/* ftp.h */ + +#ifndef _ftp_h_ +#define _ftp_h_ + +/* $RCSfile: ftp.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/07/09 11:04:12 $ + */ + +#define IS_FILE 1 +#define IS_STREAM 0 +#define IS_PIPE -1 + +/* Progress-meter types. */ +#define pr_none 0 +#define pr_percent 1 +#define pr_philbar 2 +#define pr_kbytes 3 +#define pr_dots 4 +#define pr_last pr_dots + +/* Values sent to CommandWithFlags() to determine whether to read a reply + * from the remote host after sending the command. + */ +#define DONT_WAIT_FOR_REPLY 0 +#define WAIT_FOR_REPLY 1 + +/* Expect EOF values for getreply() */ +#define DONT_EXPECT_EOF 0 +#define EXPECT_EOF 1 + +int hookup(char *, unsigned int); +int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit); +void cmdabort SIG_PARAMS; +int CommandWithFlags(char *, int); +int command(char *); +int command_noreply(char *); +int quiet_command(char *); +int verbose_command(char *); +int getreply(int); +int start_progress(int, char *); +int progress_report(int); +void end_progress(char *, char *, char *); +void close_file(FILE **, int); +void abortsend SIG_PARAMS; +int sendrequest(char *, char *, char *); +void abortrecv SIG_PARAMS; +void GetLSRemoteDir(char *, char *); +int AdjustLocalFileName(char *); +int SetToAsciiForLS(int, int); +int IssueCommand(char *, char *); +FILE *OpenOutputFile(int, char *, char *, Sig_t *); +void ReceiveBinary(FILE *, FILE *, int *, char *); +void AddRedirLine(char *); +void ReceiveAscii(FILE *, FILE *, int *, char *, int); +void CloseOutputFile(FILE *, int, char *, time_t); +void ResetOldType(int); +int FileType(char *); +void CloseData(void); +int recvrequest(char *, char *, char *, char *); +int initconn(void); +FILE *dataconn(char *); + +#endif /* _ftp_h_ */ + +/* eof ftp.h */ diff --git a/usr.bin/ncftp/ftprc.c b/usr.bin/ncftp/ftprc.c new file mode 100644 index 000000000000..e8380cef6e0e --- /dev/null +++ b/usr.bin/ncftp/ftprc.c @@ -0,0 +1,612 @@ +/* ftprc.c */ + +/* $RCSfile: ftprc.c,v $ + * $Revision: 14020.11 $ + * $Date: 93/07/09 10:58:37 $ + */ + +#include "sys.h" + +#include <sys/stat.h> + +#include <ctype.h> +#include <signal.h> + +#include "util.h" +#include "ftprc.h" +#include "main.h" +#include "cmds.h" +#include "set.h" +#include "defaults.h" +#include "copyright.h" + +/* ftprc.c global variables */ +siteptr firstsite = NULL, lastsite = NULL; +recentsite recents[dMAXRECENTS]; +int nRecents = 0; +int nSites = 0; +int keep_recent = dRECENT_ON; +longstring rcname; +longstring recent_file; +int parsing_rc = 0; + +extern char *line, *margv[]; +extern int margc, fromatty; +extern string anon_password; /* most likely your email address */ +extern string pager; +extern struct userinfo uinfo; + +int thrash_rc(void) +{ + struct stat st; + string word, str; + longstring cwd; + char *cp, *dp, *rc; + FILE *fp; + int i; + + (void) get_cwd(cwd, sizeof(cwd)); + if (cwd[strlen(cwd) - 1] != '/') + (void) Strncat(cwd, "/"); + + /* Because some versions of regular ftp complain about ncftp's + * #set commands, FTPRC takes precedence over NETRC. + */ + cp = getenv("DOTDIR"); + for (i=0; i<2; i++) { + rc = (i == 0) ? FTPRC : NETRC; + + (void) sprintf(rcname, "%s%s", cwd, rc); + if (stat(rcname, &st) == 0) + goto foundrc; + + (void) sprintf(rcname, "%s.%s", cwd, rc); + if (stat(rcname, &st) == 0) + goto foundrc; + + if (cp != NULL) { + (void) sprintf(rcname, "%s/.%s", cp, rc); + if (stat(rcname, &st) == 0) + goto foundrc; + } + + (void) sprintf(rcname, "%s/.%s", uinfo.homedir, rc); + if (stat(rcname, &st) == 0) + goto foundrc; + } + + return (0); /* it's OK not to have an rc. */ + +foundrc: + if ((st.st_mode & 077) != 0) /* rc must be unreadable by others. */ + (void) chmod(rcname, 0600); + + if ((fp = fopen(rcname, "r")) == NULL) { + PERROR("thrash_rc", rcname); + return -1; + } + + parsing_rc = 1; + while ((cp = FGets(str, fp)) != 0) { + while (isspace(*cp)) ++cp; /* skip leading space. */ + if (*cp == '#') { + if ((strncmp("set", ++cp, (size_t)3) == 0) || (strncmp("unset", cp, (size_t)5) == 0)) { + (void) strcpy(line, cp); + makeargv(); + (void) set(margc, margv); + /* setting or unsetting a variable. */ + } /* else a comment. */ + } else { + if (strncmp(cp, "machine", (size_t) 7) == 0) { + /* We have a new machine record. */ + cp += 7; + while (isspace(*cp)) ++cp; /* skip delimiting space. */ + dp = word; + while (*cp && !isspace(*cp)) *dp++ = *cp++; /* copy the name. */ + *dp = 0; + AddNewSitePtr(word); + } + } + } + (void) fclose(fp); + parsing_rc = 0; + return 1; +} /* thrash_rc */ + + + + +void AddNewSitePtr(char *word) +{ + siteptr s; + + if ((s = (siteptr) malloc(sizeof(site))) != 0) { + s->next = NULL; + if ((s->name = malloc(strlen(word) + 1)) != 0) { + (void) strcpy(s->name, word); + if (firstsite == NULL) + firstsite = lastsite = s; + else { + lastsite->next = s; + lastsite = s; + } + ++nSites; + } else { + Free(s); + } + } +} /* AddNewSitePtr */ + + + + +static int RecentCmp(recentsite *a, recentsite *b) +{ + int i = 1; + + if (a->lastcall > b->lastcall) + i = -1; + else if (a->lastcall == b->lastcall) + i = 0; + return i; +} /* RecentCmp */ + + + + +static siteptr FindNetrcSite(char *host, int exact) +{ + register siteptr s, s2; + string str, host2; + + (void) Strncpy(host2, host); + StrLCase(host2); + + /* see if 'host' is in our list of favorite sites (in NETRC). */ + for (s = firstsite; s != NULL; s2=s->next, s=s2) { + (void) Strncpy(str, s->name); + StrLCase(str); + if (exact) { + if (strcmp(str, host2) == 0) + return s; + } else { + if (strstr(str, host2) != NULL) + return s; + } + } + return NULL; +} /* FindNetrcSite */ + + + + +static recentsite *FindRecentSite(char *host, int exact) +{ + register recentsite *r; + register int i; + string str, host2; + + (void) Strncpy(host2, host); + StrLCase(host2); + + /* see if 'host' is in our list of favorite sites (in recent-log). */ + for (i=0; i<nRecents; i++) { + r = &recents[i]; + (void) Strncpy(str, r->name); + StrLCase(str); + if (exact) { + if (strcmp(str, host2) == 0) + return r; + } else { + if (strstr(str, host2) != NULL) + return r; + } + } + return NULL; +} /* FindRecentSite */ + + + + +void ReadRecentSitesFile(void) +{ + FILE *rfp; + recentsite *r; + char name[64]; + int offset; + longstring str; + + nRecents = 0; + if (recent_file[0] != 0 && keep_recent) { + rfp = fopen(recent_file, "r"); + if (rfp != NULL) { + for (; nRecents < dMAXRECENTS; ) { + r = &recents[nRecents]; + if (FGets(str, rfp) == NULL) + break; + (void) RemoveTrailingNewline(str, NULL); + name[0] = 0; + offset = 45; + if (sscanf(str, "%s %lu %n", + name, + (unsigned long *) &r->lastcall, + &offset) >= 2) + { + if ((r->name = NewString(name)) != NULL) { + r->dir = NewString(str + offset); + if (r->dir != NULL) + nRecents++; + else { + free(r->name); + r->name = r->dir = NULL; + } + } + } + } + (void) fclose(rfp); + } + } +} /* ReadRecentSitesFile */ + + + +static void SortRecentList(void) +{ + QSort(recents, nRecents, sizeof(recentsite), RecentCmp); +} /* SortRecentList */ + + + + +void WriteRecentSitesFile(void) +{ + FILE *rfp; + recentsite *r; + int i; + + if ((recent_file[0] != 0) && (nRecents > 0) && (keep_recent)) { + dbprintf("Attempting to write %s...\n", recent_file); + rfp = fopen(recent_file, "w"); + if (rfp != NULL) { + SortRecentList(); + for (i=0; i<nRecents; i++) { + r = &recents[i]; + (void) fprintf(rfp, "%-32s %11lu %s\n", r->name, + (unsigned long) r->lastcall, r->dir); + } + (void) fclose(rfp); + dbprintf("%s written successfully.\n", recent_file); + (void) chmod(recent_file, 0600); + } else { + perror(recent_file); + } + } +} /* WriteRecentSitesFile */ + + + + +void AddRecentSite(char *host, char *lastdir) +{ + char *nhost, *ndir; + recentsite *r; + + if (keep_recent) { + nhost = NewString(host); + /* Use '/' to denote that the current directory wasn't known, + * because we won't try to cd to the root directory. + */ + ndir = NewString(*lastdir ? lastdir : "/"); + + /* Don't bother if we don't have the memory, or if it is already + * in our NETRC. + */ + if ((ndir != NULL) && (nhost != NULL) && + (FindNetrcSite(host, 1) == NULL)) { + if (nRecents == dMAXRECENTS) { + SortRecentList(); + r = &recents[dMAXRECENTS - 1]; + if (r->name != NULL) + free(r->name); + if (r->dir != NULL) + free(r->dir); + } else { + r = &recents[nRecents]; + nRecents++; + } + r->name = nhost; + r->dir = ndir; + (void) time(&r->lastcall); + SortRecentList(); + } + } +} /* AddRecentSite */ + + + + +/* + * After you are done with a site (by closing it or quitting), we + * need to update the list of recent sites called. + */ +void UpdateRecentSitesList(char *host, char *lastdir) +{ + recentsite *r; + char *ndir; + + if (keep_recent) { + r = FindRecentSite(host, 1); + if (r == NULL) + AddRecentSite(host, lastdir); + else { + /* Update the last time connected, and the directory we left in. */ + if ((ndir = NewString(*lastdir ? lastdir : "/")) != NULL) { + free(r->dir); + r->dir = ndir; + } + (void) time(&r->lastcall); + } + } +} /* UpdateRecentSitesList */ + + + +/* + * Prints out the number of sites we know about, so the user can figure out + * an abbreviation or type it's number to open (setpeer). + */ +void PrintSiteList(void) +{ + int i, j; + siteptr s, s2; + FILE *pagerfp; + Sig_t sigpipe; + + if (fromatty) { + j = 0; + i = 1; + sigpipe = Signal(SIGPIPE, SIG_IGN); + if ((pagerfp = popen(pager + 1, "w")) == NULL) + pagerfp = stdout; + if (nRecents > 0) { + j++; + (void) fprintf(pagerfp, "\nRecently called sites:\n"); + for (; i<=nRecents; i++) { + (void) fprintf(pagerfp, "%4d. %-32s", i, recents[i-1].name); + i++; + if (i <= nRecents) { + (void) fprintf(pagerfp, "%5d. %-32s", i, recents[i-1].name); + } else { + (void) fprintf(pagerfp, "\n"); + break; + } + (void) fprintf(pagerfp, "\n"); + } + } + if (nSites > 0) { + j++; + (void) fprintf(pagerfp, "Sites in your netrc (%s):\n", rcname); + for (s = firstsite; s != NULL; s2=s->next, s=s2, ++i) { + (void) fprintf(pagerfp, "%4d. %-32s", i, s->name); + s2=s->next; + s=s2; + i++; + if (s != NULL) { + (void) fprintf(pagerfp, "%5d. %-32s", i, s->name); + } else { + (void) fprintf(pagerfp, "\n"); + break; + } + (void) fprintf(pagerfp, "\n"); + } + } + if (j > 0) { + (void) fprintf(pagerfp, "\ +Note that you can specify an abbreviation of any name, or #x, where x is the\n\ +number of the site you want to connect to.\n\n"); + } + + if (pagerfp != stdout) + (void) pclose(pagerfp); + Signal(SIGPIPE, sigpipe); + } +} /* PrintRecentSiteList */ + + + + +/* + * Given a sitename, check to see if the name was really an abbreviation + * of a site in the NETRC, or a site in our list of recently connected + * sites. Also check if the name was in the format #x, where x which + * would mean to use recents[x].name as the site; if x was greater than + * the number of sites in the recent list, then index into the netrc + * site list. + */ +#include <netdb.h> +void GetFullSiteName(char *host, char *lastdir) +{ + register siteptr s, s2; + register recentsite *r; + char *ndir, *nhost, *cp; + int x, i, isAllDigits, exact; + struct hostent *hostentp; + + ndir = nhost = NULL; + x = exact = 0; + + /* First, see if the "abbreviation" is really just the name of + * a machine in the local domain, or if it was a full hostname + * already. That way we can avoid problems associated with + * short names, such as having "ce" as a machine in the local + * domain, but having "faces.unl.edu", where we would most likely + * want to use ce instead of faces if the user said "open ce". + * This will also prevent problems when you have a host named + * xx.yy.unl.edu, and another host named yy.unl.edu. If the user + * said "open yy.unl.edu" we should use yy.unl.edu, and not look + * for matches containing yy.unl.edu. + */ + if ((hostentp = gethostbyname(host)) != NULL) { + strcpy(host, hostentp->h_name); + exact = 1; + } + + /* Don't allow just numbers as abbreviations; "open 2" could be + * confused between site numbers in the open 'menu,' like + * "2. unlinfo.unl.edu" and IP numbers "128.93.2.1" or even numbers + * in the site name like "simtel20.army.mil." + */ + + for (isAllDigits = 1, cp = host; *cp != 0; cp++) { + if (!isdigit(*cp)) { + isAllDigits = 0; + break; + } + } + + if (!isAllDigits) { + if (host[0] == '#') + (void) sscanf(host + 1, "%d", &x); + /* Try matching the abbreviation, since it isn't just a number. */ + /* see if 'host' is in our list of favorite sites (in NETRC). */ + + if (x == 0) { + if ((s = FindNetrcSite(host, exact)) != NULL) { + nhost = s->name; + } else if ((r = FindRecentSite(host, exact)) != NULL) { + nhost = r->name; + ndir = r->dir; + } + } + } else if (sscanf(host, "%d", &x) != 1) { + x = 0; + } + + if (--x >= 0) { + if (x < nRecents) { + nhost = recents[x].name; + ndir = recents[x].dir; + } else { + x -= nRecents; + if (x < nSites) { + for (i = 0, s = firstsite; i < x; s2=s->next, s=s2) + ++i; + nhost = s->name; + } + } + } + + if (nhost != NULL) { + (void) strcpy(host, nhost); + if (lastdir != NULL) { + *lastdir = 0; + /* Don't cd if the dir is the root directory. */ + if (ndir != NULL && (strcmp("/", ndir) != 0)) + (void) strcpy(lastdir, ndir); + } + } +} /* GetFullSiteName */ + + + + +int ruserpass2(char *host, char **username, char **pass, char **acct) +{ + FILE *fp; + char *cp, *dp, *dst, *ep; + str32 macname; + char *varname; + int site_found; + string str; + static string auser; + static str32 apass, aacct; + + site_found = 0; + + if ((fp = fopen(rcname, "r")) != NULL) { + parsing_rc = 1; + while (FGets(str, fp)) { + if ((cp = strstr(str, "machine")) != 0) { + /* Look for the machine token. */ + cp += 7; + while (isspace(*cp)) + cp++; + } else + continue; + if (strncmp(host, cp, strlen(host)) == 0) { + site_found = 1; + while (!isspace(*cp)) + ++cp; /* skip the site name. */ + do { + /* Skip any comments ahead of time. */ + for (dp=cp; *dp; dp++) { + if (*dp == '#') { + *dp = 0; + break; + } + } + + ep = cp; + while (1) { + varname = strtok(ep, RC_DELIM); + if (!varname) break; + dst = ep = NULL; + switch (*varname) { + case 'u': /* user */ + *username = dst = auser; + break; + case 'l': /* login */ + *username = dst = auser; + break; + case 'p': /* password */ + *pass = dst = apass; + break; + case 'a': /* account */ + *acct = dst = aacct; + break; + /* case 'd': /o default */ + /* unused -- use 'set anon_password.' */ + case 'm': /* macdef or machine */ + if (strcmp(varname, "macdef")) + goto done; /* new machine record... */ + dst = macname; + break; + default: + (void) fprintf(stderr, "Unknown .netrc keyword \"%s\"\n", + varname + ); + } + if (dst) { + dp = strtok(ep, RC_DELIM); + if (dp) + (void) strcpy(dst, dp); + if (dst == macname) { + /* + * Read in the lines of the macro. + * The macro's end is denoted by + * a blank line. + */ + (void) make_macro(macname, fp); + goto nextline; + } + } + } +nextline: ; + } while ((cp = FGets(str, fp)) != 0); + break; + } /* end if we found the machine record. */ + } +done: + parsing_rc = 0; + (void) fclose(fp); + } + + if (!site_found) { + /* didn't find it in the rc. */ + return (0); + } + + return (1); /* found */ +} /* ruserpass2 */ + +/* eof ftprc.c */ diff --git a/usr.bin/ncftp/ftprc.h b/usr.bin/ncftp/ftprc.h new file mode 100644 index 000000000000..14eec88ad860 --- /dev/null +++ b/usr.bin/ncftp/ftprc.h @@ -0,0 +1,39 @@ +/* ftprc.h */ + +#ifndef _ftprc_h_ +#define _ftprc_h_ + +/* $RCSfile: ftprc.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:45:31 $ + */ + +#define NETRC "netrc" +#define FTPRC "ncftprc" + +#define RC_DELIM " \n\t," + +typedef struct site *siteptr; +typedef struct site { + char *name; /* name (or IP address) of site */ + siteptr next; +} site; + +typedef struct recentsite { + char *name; /* name (or IP address) of site */ + char *dir; /* directory we were in last time we called. */ + time_t lastcall; /* when this site was called last. */ +} recentsite; + +int thrash_rc(void); +void AddNewSitePtr(char *word); +int ruserpass2(char *host, char **user, char **pass, char **acct); +void GetFullSiteName(char *host, char *lastdir); +void ReadRecentSitesFile(void); +void WriteRecentSitesFile(void); +void AddRecentSite(char *host, char *lastdir); +void UpdateRecentSitesList(char *host, char *lastdir); +void PrintSiteList(void); + +#endif +/* eof */ diff --git a/usr.bin/ncftp/getpass.c b/usr.bin/ncftp/getpass.c new file mode 100644 index 000000000000..a039e7a94f54 --- /dev/null +++ b/usr.bin/ncftp/getpass.c @@ -0,0 +1,160 @@ +/* Getpass.c */ + +/* $RCSfile: getpass.c,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:44:36 $ + */ + +#include "sys.h" + +#include <signal.h> + +#include "util.h" +#include "cmds.h" +#include "getpass.h" +#include "copyright.h" + +#ifndef GETPASS + +#ifndef sun /* ...both unnecessary, and conflicting with <termios.h> */ +#include <sys/ioctl.h> +#endif + +#ifdef TERMIOS +# include <termios.h> +#else +# ifdef SGTTYB +# include <sgtty.h> +# else +# include <termio.h> +# endif +#endif /* !TERMIOS */ + +#ifdef STRICT_PROTOS +int ioctl(int, int, ...); +#endif + +#endif /* GETPASS */ + + + + +void Echo(FILE *fp, int on) +{ +#ifndef GETPASS /* Otherwise just do nothing which is ok. */ + +#ifdef TERMIOS + static struct termios orig, noecho, *tp; +#else +# ifdef SGTTYB + static struct sgttyb orig, noecho, *tp; +# else + static struct termio orig, noecho, *tp; +# endif +#endif + static int state = 0; + int fd = fileno(fp); + + if (!isatty(fd)) + return; + + if (state == 0) { +#ifdef TERMIOS + if (tcgetattr(fd, &orig) < 0) + PERROR("echo", "tcgetattr"); + noecho = orig; + noecho.c_lflag &= ~ECHO; +#else +# ifdef SGTTYB + if (ioctl(fd, TIOCGETP, &orig) < 0) + PERROR("echo", "ioctl"); + noecho = orig; + noecho.sg_flags &= ~ECHO; +# else + if (ioctl(fd, TCGETA, &orig) < 0) + PERROR("echo", "ioctl"); + noecho = orig; + noecho.c_lflag &= ~ECHO; +# endif +#endif + state = 1; + } + tp = NULL; + if (on && state == 2) { + /* Turn echo back on. */ + tp = &orig; + state = 1; + } else if (!on && state == 1) { + /* Turn echo off. */ + tp = &noecho; + state = 2; + } + if (tp != NULL) { +#ifdef TERMIOS + if (tcsetattr(fd, TCSANOW, tp) < 0) + PERROR("echo", "tcsetattr"); +#else +# ifdef SGTTYB + if (ioctl(fd, TIOCSETP, tp) < 0) + PERROR("echo", "ioctl"); +# else + if (ioctl(fd, TCSETA, tp) < 0) + PERROR("echo", "ioctl"); +# endif +#endif /* !TERMIOS */ + } + +#endif /* GETPASS */ +} /* Echo */ + + + +#ifndef GETPASS + +char *Getpass(char *promptstr) +{ + register int ch; + register char *p; + FILE *fp, *outfp; + Sig_t oldintr; + static char buf[kMaxPassLen + 1]; + + /* + * read and write to /dev/tty if possible; else read from + * stdin and write to stderr. + */ +#if !defined(BOTCHED_FOPEN_RW) + if ((outfp = fp = fopen("/dev/tty", "w+")) == NULL) { + outfp = stderr; + fp = stdin; + } +#else + /* SCO 32v2 botches "w+" open */ + if ((fp = fopen("/dev/tty", "r")) == NULL) + fp = stdin; + if ((outfp = fopen("/dev/tty", "w")) == NULL) + outfp = stderr; +#endif + oldintr = Signal(SIGINT, SIG_IGN); + Echo(fp, 0); /* Turn echoing off. */ + (void) fputs(promptstr, outfp); + (void) rewind(outfp); /* implied flush */ + for (p = buf; (ch = getc(fp)) != EOF && ch != '\n';) + if (p < buf + kMaxPassLen) + *p++ = ch; + *p = '\0'; + (void)write(fileno(outfp), "\n", 1); + Echo(fp, 1); + (void) Signal(SIGINT, oldintr); + if (fp != stdin) + (void)fclose(fp); +#if defined(BOTCHED_FOPEN_RW) + if (outfp != stderr) + (void)fclose(outfp); +#endif + return(buf); +} /* Getpass */ + +#endif /* GETPASS */ + +/* eof Getpass.c */ diff --git a/usr.bin/ncftp/getpass.h b/usr.bin/ncftp/getpass.h new file mode 100644 index 000000000000..c8d358c012bf --- /dev/null +++ b/usr.bin/ncftp/getpass.h @@ -0,0 +1,23 @@ +/* Getpass.h */ + +#ifndef _getpass_h_ +#define _getpass_h_ + +/* $RCSfile: getpass.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:45:36 $ + */ + +#define kMaxPassLen 127 + +#ifdef GETPASS +extern char *getpass(); /* Use the system supplied getpass. */ +#else +char *Getpass(char *prompt); +#endif + +void Echo(FILE *fp, int on); + +#endif /* _getpass_h_ */ + +/* eof Getpass.h */ diff --git a/usr.bin/ncftp/glob.c b/usr.bin/ncftp/glob.c new file mode 100644 index 000000000000..b0f31d13d096 --- /dev/null +++ b/usr.bin/ncftp/glob.c @@ -0,0 +1,655 @@ +/* glob.c */ + +/* $RCSfile: glob.c,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:44:32 $ + */ + +#include "sys.h" + +#include <sys/stat.h> + +/* Dir.h. Try <sys/dir.h> (add -DSYSDIRH) if <dirent.h> doesn't exist. */ + +#ifndef SYSDIRH +# include <dirent.h> +#else +# include <sys/dir.h> +#endif + +#ifdef SCO324 +# define direct dirent +#endif + +#include <errno.h> +#include <pwd.h> +#include "util.h" +#include "glob.h" +#include "cmds.h" +#include "copyright.h" + +#ifndef NCARGS +# define NCARGS 4096 /* # characters in exec arglist */ +#endif + +#define L_CURLY '{' +#define R_CURLY '}' + +#define QUOTE 0200 +#define TRIM 0177 +#define eq(a,b) (strcmp(a, b)==0) +#define GAVSIZ (NCARGS/6) +#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) + +static void ginit(char **agargv); +static void collect(char *as); +static void acollect(char *as); +static void sort(void); +static void expand(char *as); +static void matchdir(char *pattern); +static int execbrc(char *p, char *s); +static match(char *s, char *p); +static amatch(char *s, char *p); +#if UNUSED +static Gmatch(char *s, char *p); +#endif +static void Gcat(char *s1, char *s2); +static void addpath(char c); +static void rscan(char **t, int (*f )(char)); +static tglob(char c); +static char *strspl(char *cp, char *dp); +static char *strend(char *cp); + +static char **gargv; /* Pointer to the (stack) arglist */ +static int gargc; /* Number args in gargv */ +static int gnleft; +static short gflag; +char *globerr; +char *home; /* you must initialize this elsewhere! */ +extern int errno; + +static int globcnt; + +char *globchars = "`{[*?"; + +static char *gpath, *gpathp, *lastgpathp; +static int globbed; +static char *entp; +static char **sortbas; + +char ** +glob(char *v) +{ + char agpath[BUFSIZ]; + char *agargv[GAVSIZ]; + char *vv[2]; + vv[0] = v; + vv[1] = 0; + gflag = (short) 0; + rscan(vv, tglob); + if (gflag == (short) 0) + return (copyblk(vv)); + + globerr = 0; + gpath = agpath; gpathp = gpath; *gpathp = 0; + lastgpathp = &gpath[sizeof agpath - 2]; + ginit(agargv); globcnt = 0; + collect(v); + if (globcnt == 0 && (gflag & (short)1)) { + blkfree(gargv), gargv = 0; + return (0); + } else + return (gargv = copyblk(gargv)); +} + +static +void ginit(char **agargv) +{ + agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0; + gnleft = NCARGS - 4; +} + +static +void collect(char *as) +{ + if (eq(as, "{") || eq(as, "{}")) { + Gcat(as, ""); + sort(); + } else + acollect(as); +} + +static +void acollect(char *as) +{ + register int ogargc = gargc; + + gpathp = gpath; *gpathp = 0; globbed = 0; + expand(as); + if (gargc != ogargc) + sort(); +} + +static +void sort(void) +{ + register char **p1, **p2, *c; + char **Gvp = &gargv[gargc]; + + p1 = sortbas; + while (p1 < Gvp-1) { + p2 = p1; + while (++p2 < Gvp) + if (strcmp(*p1, *p2) > 0) + c = *p1, *p1 = *p2, *p2 = c; + p1++; + } + sortbas = Gvp; +} + +static +void expand(char *as) +{ + register char *cs; + register char *sgpathp, *oldcs; + struct stat stb; + + sgpathp = gpathp; + cs = as; + if (*cs == '~' && gpathp == gpath) { + addpath('~'); + for (cs++; letter(*cs) || digit(*cs) || *cs == '-';) + addpath(*cs++); + if (!*cs || *cs == '/') { + if (gpathp != gpath + 1) { + *gpathp = 0; + if (gethdir(gpath + 1)) + globerr = "Unknown user name after ~"; + (void) strcpy(gpath, gpath + 1); + } else + (void) strcpy(gpath, home); + gpathp = strend(gpath); + } + } + while (!any(*cs, globchars)) { + if (*cs == 0) { + if (!globbed) + Gcat(gpath, ""); + else if (stat(gpath, &stb) >= 0) { + Gcat(gpath, ""); + globcnt++; + } + goto endit; + } + addpath(*cs++); + } + oldcs = cs; + while (cs > as && *cs != '/') + cs--, gpathp--; + if (*cs == '/') + cs++, gpathp++; + *gpathp = 0; + if (*oldcs == L_CURLY) { + (void) execbrc(cs, ((char *)0)); + return; + } + matchdir(cs); +endit: + gpathp = sgpathp; + *gpathp = 0; +} + +static +void matchdir(char *pattern) +{ + struct stat stb; +#ifdef SYSDIRH + register struct direct *dp; +#else + register struct dirent *dp; +#endif + DIR *dirp; + + dirp = opendir((*gpath ? gpath : ".")); + if (dirp == NULL) { + if (globbed) + return; + goto patherr2; + } + if (fstat(dirp->dd_fd, &stb) < 0) + goto patherr1; + if (!isdir(stb)) { + errno = ENOTDIR; + goto patherr1; + } + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_ino == 0) + continue; + if (match(dp->d_name, pattern)) { + Gcat(gpath, dp->d_name); + globcnt++; + } + } + (void) closedir(dirp); + return; + +patherr1: + (void) closedir(dirp); +patherr2: + globerr = "Bad directory components"; +} + +static +int execbrc(char *p, char *s) +{ + char restbuf[BUFSIZ + 2]; + register char *pe, *pm, *pl; + int brclev = 0; + char *lm, savec, *sgpathp; + + for (lm = restbuf; *p != L_CURLY; *lm++ = *p++) + continue; + for (pe = ++p; *pe; pe++) + switch (*pe) { + + case L_CURLY: + brclev++; + continue; + + case R_CURLY: + if (brclev == 0) + goto pend; + brclev--; + continue; + + case '[': + for (pe++; *pe && *pe != ']'; pe++) + continue; + continue; + } +pend: + brclev = 0; + for (pl = pm = p; pm <= pe; pm++) + switch (*pm & (QUOTE|TRIM)) { + + case L_CURLY: + brclev++; + continue; + + case R_CURLY: + if (brclev) { + brclev--; + continue; + } + goto doit; + + case ','|QUOTE: + case ',': + if (brclev) + continue; +doit: + savec = *pm; + *pm = 0; + (void) strcpy(lm, pl); + (void) strcat(restbuf, pe + 1); + *pm = savec; + if (s == 0) { + sgpathp = gpathp; + expand(restbuf); + gpathp = sgpathp; + *gpathp = 0; + } else if (amatch(s, restbuf)) + return (1); + sort(); + pl = pm + 1; + if (brclev) + return (0); + continue; + + case '[': + for (pm++; *pm && *pm != ']'; pm++) + continue; + if (!*pm) + pm--; + continue; + } + if (brclev) + goto doit; + return (0); +} + +static +int match(char *s, char *p) +{ + register int c; + register char *sentp; + char sglobbed = globbed; + + if (*s == '.' && *p != '.') + return (0); + sentp = entp; + entp = s; + c = amatch(s, p); + entp = sentp; + globbed = sglobbed; + return (c); +} + +static +int amatch(char *s, char *p) +{ + register int scc; + int ok, lc; + char *sgpathp; + struct stat stb; + int c, cc; + + globbed = 1; + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case L_CURLY: + return (execbrc(p - 1, s - 1)); + + case '[': + ok = 0; + lc = 077777; + while ((cc = *p++) != '\0') { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + if (ok) + p--; + else + return 0; + continue; + + case '*': + if (!*p) + return (1); + if (*p == '/') { + p++; + goto slash; + } + s--; + do { + if (amatch(s, p)) + return (1); + } while (*s++); + return (0); + + case 0: + return (scc == 0); + + default: + if (c != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + case '/': + if (scc) + return (0); +slash: + s = entp; + sgpathp = gpathp; + while (*s) + addpath(*s++); + addpath('/'); + if (stat(gpath, &stb) == 0 && isdir(stb)) + if (*p == 0) { + Gcat(gpath, ""); + globcnt++; + } else + expand(p); + gpathp = sgpathp; + *gpathp = 0; + return (0); + } + } +} + +#if UNUSED +static +Gmatch(char *s, char *p) +{ + register int scc; + int ok, lc; + int c, cc; + + for (;;) { + scc = *s++ & TRIM; + switch (c = *p++) { + + case '[': + ok = 0; + lc = 077777; + while (cc = *p++) { + if (cc == ']') { + if (ok) + break; + return (0); + } + if (cc == '-') { + if (lc <= scc && scc <= *p++) + ok++; + } else + if (scc == (lc = cc)) + ok++; + } + if (cc == 0) + if (ok) + p--; + else + return 0; + continue; + + case '*': + if (!*p) + return (1); + for (s--; *s; s++) + if (Gmatch(s, p)) + return (1); + return (0); + + case 0: + return (scc == 0); + + default: + if ((c & TRIM) != scc) + return (0); + continue; + + case '?': + if (scc == 0) + return (0); + continue; + + } + } +} +#endif + +static +void Gcat(char *s1, char *s2) +{ + register int len = strlen(s1) + strlen(s2) + 1; + + if (len >= gnleft || gargc >= GAVSIZ - 1) + globerr = "Arguments too long"; + else { + gargc++; + gnleft -= len; + gargv[gargc] = 0; + gargv[gargc - 1] = strspl(s1, s2); + } +} + +static +void addpath(char c) +{ + + if (gpathp >= lastgpathp) + globerr = "Pathname too long"; + else { + *gpathp++ = c; + *gpathp = 0; + } +} + +static +void rscan(char **t, int (*f )(char)) +{ + register char *p, c; + + while ((p = *t++) != 0) { + if (f == tglob) + if (*p == '~') + gflag |= (short) 2; + else if (eq(p, "{") || eq(p, "{}")) + continue; + while ((c = *p++) != '\0') + (*f)(c); + } +} +/* +static +scan(t, f) + register char **t; + int (*f)(char); +{ + register char *p, c; + + while (p = *t++) + while (c = *p) + *p++ = (*f)(c); +} */ + +static +int tglob(char c) +{ + + if (any(c, globchars)) + gflag |= (c == L_CURLY ? (short)2 : (short)1); + return (c); +} +/* +static +trim(c) + char c; +{ + + return (c & TRIM); +} */ + + +int letter(char c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'); +} + +int digit(char c) +{ + return (c >= '0' && c <= '9'); +} + +int any(int c, char *s) +{ + while (*s) + if (*s++ == c) + return(1); + return(0); +} + +int blklen(char **av) +{ + register int i = 0; + + while (*av++) + i++; + return (i); +} + +char ** +blkcpy(char **oav, char **bv) +{ + register char **av = oav; + + while ((*av++ = *bv++) != 0) + continue; + return (oav); +} + +void blkfree(char **av0) +{ + register char **av = av0; + + while (*av) + free(*av++); +} + +static +char * +strspl(char *cp, char *dp) +{ + register char *ep = (char *) malloc((size_t)(strlen(cp) + strlen(dp) + 1L)); + + if (ep == (char *)0) + fatal("Out of memory"); + (void) strcpy(ep, cp); + (void) strcat(ep, dp); + return (ep); +} + +char ** +copyblk(char **v) +{ + register char **nv = (char **)malloc((size_t)((blklen(v) + 1) * + sizeof(char **))); + if (nv == (char **)0) + fatal("Out of memory"); + + return (blkcpy(nv, v)); +} + +static +char * +strend(char *cp) +{ + while (*cp) + cp++; + return (cp); +} + +/* + * Extract a home directory from the password file + * The argument points to a buffer where the name of the + * user whose home directory is sought is currently. + * We write the home directory of the user back there. + */ +int gethdir(char *home_dir) +{ + register struct passwd *pp = getpwnam(home_dir); + + if (pp == 0) + return (1); + (void) strcpy(home_dir, pp->pw_dir); + return (0); +} /* gethdir */ + +/* eof glob.c */ diff --git a/usr.bin/ncftp/glob.h b/usr.bin/ncftp/glob.h new file mode 100644 index 000000000000..4f35e7c91119 --- /dev/null +++ b/usr.bin/ncftp/glob.h @@ -0,0 +1,22 @@ +/* glob.h */ + +#ifndef _glob_h_ +#define _glob_h_ 1 + +/* $RCSfile: glob.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:45:32 $ + */ + +char **glob(char *v); +int letter(char c); +int digit(char c); +int any(int c, char *s); +int blklen(char **av); +char **blkcpy(char **oav, char **bv); +void blkfree(char **av0); +char **copyblk(char **v); +int gethdir(char *home_dir); + +#endif + diff --git a/usr.bin/ncftp/main.c b/usr.bin/ncftp/main.c new file mode 100644 index 000000000000..446c1c3a508b --- /dev/null +++ b/usr.bin/ncftp/main.c @@ -0,0 +1,1128 @@ +/* main.c + * + * $RCSfile: main.c,v $ + * $Revision: 14020.15 $ + * $Date: 93/07/09 11:50:12 $ + */ + +#define _main_c_ + +#define FTP_VERSION "1.8.5 (September 20, 1994)" + +/* #define BETA 1 */ /* If defined, it prints a little warning message. */ + +#include "sys.h" + +#include <sys/stat.h> +#include <arpa/ftp.h> +#include <setjmp.h> +#include <signal.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <pwd.h> + +#ifdef SYSLOG +# include <syslog.h> +#endif + +#if defined(CURSES) && !defined(NO_CURSES_H) +# undef HZ /* Collides with HaZeltine ! */ +# include <curses.h> +# ifdef TERMH +# include <term.h> +# endif +#endif /* CURSES */ + +#include "util.h" +#include "cmds.h" +#include "main.h" +#include "ftp.h" +#include "ftprc.h" +#include "open.h" +#include "set.h" +#include "defaults.h" +#include "copyright.h" + +/* main.c globals */ +int slrflag; +int fromatty; /* input is from a terminal */ +int toatty; /* output is to a terminal */ +int doing_script; /* is a file being <redirected to me? */ +char *altarg; /* argv[1] with no shell-like preprocessing */ +struct servent serv; /* service spec for tcp/ftp */ +jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ +char *line; /* input line buffer */ +char *stringbase; /* current scan point in line buffer */ +char *argbuf; /* argument storage buffer */ +char *argbase; /* current storage point in arg buffer */ +int margc; /* count of arguments on input line */ +char *margv[20]; /* args parsed from input line */ +struct userinfo uinfo; /* a copy of their pwent really */ +int ansi_escapes; /* for fancy graphics */ +int startup_msg = 1; /* TAR: display message on startup? */ +int ignore_rc; /* are we supposed to ignore the netrc */ +string progname; /* simple filename */ +string prompt, prompt2; /* shell prompt string */ +string anon_password; /* most likely your email address */ +string pager; /* program to browse text files */ +string version = FTP_VERSION; +long eventnumber; /* number of commands we've done */ +FILE *logf = NULL; /* log user activity */ +longstring logfname; /* name of the logfile */ +long logsize = 4096L; /* max log size. 0 == no limit */ +int percent_flags; /* "%" in prompt string? */ +int at_flags; /* "@" in prompt string? */ +string mail_path; /* your mailbox */ +time_t mbox_time; /* last modified time of mbox */ +size_t epromptlen; /* length of the last line of the + * prompt as it will appear on screen, + * (i.e. no invis escape codes). + */ + +#ifdef HPUX +char *tcap_normal = "\033&d@"; /* Default ANSI escapes */ +char *tcap_boldface = "\033&dH"; /* Half Bright */ +char *tcap_underline = "\033&dD"; +char *tcap_reverse = "\033&dB"; + +#else + +#ifdef NO_FORMATTING + +char *tcap_normal = ""; +char *tcap_boldface = ""; +char *tcap_underline = ""; +char *tcap_reverse = ""; + +#else + +char *tcap_normal = "\033[0m"; /* Default ANSI escapes */ +char *tcap_boldface = "\033[1m"; +char *tcap_underline = "\033[4m"; +char *tcap_reverse = "\033[7m"; + +#endif + +#endif + +size_t tcl_normal = 4, /* lengths of the above strings. */ + tcl_bold = 4, + tcl_uline = 4, + tcl_rev = 4; + +#ifdef CURSES +static char tcbuf[2048]; +#endif + +/* main.c externs */ +extern int debug, verbose, mprompt; +extern int options, cpend, data, connected, logged_in; +extern int curtype, macnum, remote_is_unix; +extern FILE *cout; +extern struct cmd cmdtab[]; +extern str32 curtypename; +extern char *macbuf; +extern char *reply_string; +extern char *short_verbose_msgs[4]; +extern string vstr; +extern Hostname hostname; +extern longstring cwd, lcwd, recent_file; +extern int Optind; +extern char *Optarg; +#ifdef GATEWAY +extern string gate_login; +#endif + +void main(int argc, char **argv) +{ + register char *cp; + int top, opt, openopts = 0; + string tmp, oline; + struct servent *sptr; + + if ((cp = rindex(argv[0], '/'))) cp++; + else cp = argv[0]; + (void) Strncpy(progname, cp); + + sptr = getservbyname("ftp", "tcp"); + if (sptr == 0) fatal("ftp/tcp: unknown service"); + serv = *sptr; + + if (init_arrays()) /* Reserve large blocks of memory now */ + fatal("could not reserve large amounts of memory."); + +#ifdef GZCAT + if ((GZCAT == (char *)1) || (GZCAT == (char *)0)) { + (void) fprintf(stderr, +"You compiled the program with -DGZCAT, but you must specify the path with it!\n\ +Re-compile, this time with -DGZCAT=\\\"/path/to/gzcat\\\".\n"); + exit(1); + } +#endif +#ifdef ZCAT + if ((ZCAT == (char *)1) || (ZCAT == (char *)0)) { + (void) fprintf(stderr, +"You compiled the program with -DZCAT, but you must specify the path with it!\n\ +Re-compile, this time with -DZCAT=\\\"/path/to/zcat\\\".\n"); + exit(1); + } +#endif + + /* + * Set up defaults for FTP. + */ + mprompt = dMPROMPT; + debug = dDEBUG; + verbose = dVERBOSE; + (void) Strncpy(vstr, short_verbose_msgs[verbose+1]); + + (void) Strncpy(curtypename, dTYPESTR); + curtype = dTYPE; + (void) Strncpy(prompt, dPROMPT); +#ifdef GATEWAY + (void) Strncpy(gate_login, dGATEWAY_LOGIN); +#endif + +#ifdef SOCKS + SOCKSinit("ncftp"); +#endif + + /* Setup our pager variable, before we run through the rc, + which may change it. */ + set_pager(getenv("PAGER"), 0); +#ifdef CURSES + ansi_escapes = 1; + termcap_init(); +#else + ansi_escapes = 0; + if ((cp = getenv("TERM")) != NULL) { + if ((*cp == 'v' && cp[1] == 't') /* vt100, vt102, ... */ + || (strcmp(cp, "xterm") == 0)) + ansi_escapes = 1; + } +#endif + (void) getuserinfo(); + + /* Init the mailbox checking code. */ + (void) time(&mbox_time); + + (void) Strncpy(anon_password, uinfo.username); + if (getlocalhostname(uinfo.hostname, sizeof(uinfo.hostname)) == 0) { + (void) Strncat(anon_password, "@"); + (void) Strncat(anon_password, uinfo.hostname); + } +#if dLOGGING + (void) Strncpy(logfname, dLOGNAME); + (void) LocalDotPath(logfname); +#else + *logfname = 0; +#endif + (void) Strncpy(recent_file, dRECENTF); + (void) LocalDotPath(recent_file); + + (void) get_cwd(lcwd, (int) sizeof(lcwd)); + +#ifdef SYSLOG +# ifdef LOG_LOCAL3 + openlog ("NcFTP", LOG_PID, LOG_LOCAL3); +# else + openlog ("NcFTP", LOG_PID); +# endif +#endif /* SYSLOG */ + + + ignore_rc = 0; + (void) strcpy(oline, "open "); + while ((opt = Getopt(argc, argv, "D:V:INRHaicmup:rd:g:")) >= 0) { + switch(opt) { + case 'a': + case 'c': + case 'i': + case 'm': + case 'u': + case 'r': + (void) sprintf(tmp, "-%c ", opt); + goto cattmp; + + case 'p': + case 'd': + case 'g': + (void) sprintf(tmp, "-%c %s ", opt, Optarg); + cattmp: + (void) strcat(oline, tmp); + openopts++; + break; + + case 'D': + debug = atoi(Optarg); + break; + + case 'V': + set_verbose(Optarg, 0); + break; + + case 'I': + mprompt = !mprompt; + break; + + case 'N': + ++ignore_rc; + break; + + case 'H': + (void) show_version(0, NULL); + exit (0); + + default: + usage: + (void) fprintf(stderr, "Usage: %s [program options] [[open options] site.to.open[:path]]\n\ +Program Options:\n\ + -D x : Set debugging level to x (a number).\n\ + -H : Show version and compilation information.\n\ + -I : Toggle interactive (mprompt) mode.\n\ + -N : Toggle reading of the .netrc/.ncftprc.\n\ + -V x : Set verbosity to level x (-1,0,1,2).\n\ +Open Options:\n\ + -a : Open anonymously (this is the default).\n\ + -u : Open, specify user/password.\n\ + -i : Ignore machine entry in your .netrc.\n\ + -p N : Use port #N for connection.\n\ + -r : \"Redial\" until connected.\n\ + -d N : Redial, pausing N seconds between tries.\n\ + -g N : Redial, giving up after N tries.\n\ + :path : ``Colon-mode:'' If \"path\" is a file, it opens site, retrieves\n\ + file \"path,\" then exits; if \"path\" is a remote directory,\n\ + it opens site then starts you in that directory..\n\ + -c : If you're using colon-mode with a file path, this will cat the\n\ + file to stdout instead of storing on disk.\n\ + -m : Just like -c, only it pipes the file to your $PAGER.\n\ +Examples:\n\ + ncftp ftp.unl.edu:/pub/README (just fetches README then quits)\n\ + ncftp (just enters ncftp command shell)\n\ + ncftp -V -u ftp.unl.edu\n\ + ncftp -c ftp.unl.edu:/pub/README (cats README to stdout then quits)\n\ + ncftp -D -r -d 120 -g 10 ftp.unl.edu\n", progname); + exit(1); + } + } + + cp = argv[Optind]; /* the site to open. */ + if (cp == NULL) { + if (openopts) + goto usage; + } else + (void) strcat(oline, cp); + + if (ignore_rc <= 0) + (void) thrash_rc(); + if (ignore_rc <= 1) + ReadRecentSitesFile(); + + (void) fix_options(); /* adjust "options" according to "debug" */ + + fromatty = doing_script = isatty(0); + toatty = isatty(1); + (void) UserLoggedIn(); /* Init parent-death detection. */ + cpend = 0; /* no pending replies */ + + if (*logfname) + logf = fopen (logfname, "a"); + + + /* The user specified a host, maybe in 'colon-mode', on the command + * line. Open it now... + */ + if (argc > 1 && cp) { + if (setjmp(toplevel)) + exit(0); + (void) Signal(SIGINT, intr); + (void) Signal(SIGPIPE, lostpeer); + (void) strcpy(line, oline); + makeargv(); + /* setpeer uses this to tell if it was called from the cmd-line. */ + eventnumber = 0L; + (void) cmdOpen(margc, margv); + } + eventnumber = 1L; + + (void) init_prompt(); + + if (startup_msg) { /* TAR */ + if (ansi_escapes) { +#ifdef BETA +# define BETA_MSG "\n\ +For testing purposes only. Do not re-distribute or subject to novice users." +#else +# define BETA_MSG "" +#endif + +#ifndef CURSES + (void) printf("%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n", + tcap_boldface, + FTP_VERSION, + tcap_normal, + tcap_reverse, + BETA_MSG, + tcap_normal + ); +#else + char vis[256]; + (void) sprintf(vis, "%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n", + tcap_boldface, + FTP_VERSION, + tcap_normal, + tcap_reverse, + BETA_MSG, + tcap_normal + ); + tcap_put(vis); +#endif /* !CURSES */ + } + else + (void) printf("%s%s\n", FTP_VERSION, BETA_MSG); + } /* TAR */ + if (NOT_VQUIET) + PrintTip(); + top = setjmp(toplevel) == 0; + if (top) { + (void) Signal(SIGINT, intr); + (void) Signal(SIGPIPE, lostpeer); + } + for (;;) { + (void) cmdscanner(top); + top = 1; + } +} /* main */ + + + +/*ARGSUSED*/ +void intr SIG_PARAMS +{ + dbprintf("intr()\n"); + (void) Signal(SIGINT, intr); + (void) longjmp(toplevel, 1); +} /* intr */ + + + +int getuserinfo(void) +{ + register char *cp; + struct passwd *pw; + string str; + extern char *home; /* for glob.c */ + + home = uinfo.homedir; /* for glob.c */ + pw = NULL; +#ifdef USE_GETPWUID + /* Try to use getpwuid(), but if we have to, fall back to getpwnam(). */ + pw = getpwuid(getuid()); + if (pw == NULL) { + /* Oh well, try getpwnam() then. */ + cp = getlogin(); + if (cp == NULL) { + cp = getenv("LOGNAME"); + if (cp == NULL) + cp = getenv("USER"); + } + if (cp != NULL) + pw = getpwnam(cp); + } +#else + /* Try to use getpwnam(), but if we have to, fall back to getpwuid(). */ + cp = getlogin(); + if (cp == NULL) { + cp = getenv("LOGNAME"); + if (cp == NULL) + cp = getenv("USER"); + } + if (cp != NULL) + pw = getpwnam(cp); + if (pw == NULL) { + /* Oh well, try getpwuid() then. */ + pw = getpwuid(getuid()); + } +#endif + if (pw != NULL) { + uinfo.uid = pw->pw_uid; + (void) Strncpy(uinfo.username, pw->pw_name); + (void) Strncpy(uinfo.shell, pw->pw_shell); + if ((cp = getenv("HOME")) != NULL) + (void) Strncpy(uinfo.homedir, cp); + else + (void) Strncpy(uinfo.homedir, pw->pw_dir); + cp = getenv("MAIL"); + if (cp == NULL) + cp = getenv("mail"); + if (cp == NULL) + (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username); + else + (void) Strncpy(str, cp); + cp = str; + + /* + * mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you), + * so try to find the first mail path. + */ + while ((*cp != '/') && (*cp != 0)) + cp++; + (void) Strncpy(mail_path, cp); + if ((cp = index(mail_path, ' ')) != NULL) + *cp = '\0'; + return (0); + } else { + PERROR("getuserinfo", "Could not get your passwd entry!"); + (void) Strncpy(uinfo.shell, "/bin/sh"); + (void) Strncpy(uinfo.homedir, "."); /* current directory */ + uinfo.uid = 999; + if ((cp = getenv("HOME")) != NULL) + (void) Strncpy(uinfo.homedir, cp); + mail_path[0] = 0; + return (-1); + } +} /* getuserinfo */ + + + + +int init_arrays(void) +{ + if ((macbuf = (char *) malloc((size_t)(MACBUFLEN))) == NULL) + goto barf; + if ((line = (char *) malloc((size_t)(CMDLINELEN))) == NULL) + goto barf; + if ((argbuf = (char *) malloc((size_t)(CMDLINELEN))) == NULL) + goto barf; + if ((reply_string = (char *) malloc((size_t)(RECEIVEDLINELEN))) == NULL) + goto barf; + + *macbuf = '\0'; + init_transfer_buffer(); + return (0); +barf: + return (-1); +} /* init_arrays */ + + + +#ifndef BUFSIZ +#define BUFSIZ 512 +#endif + +void init_transfer_buffer(void) +{ + extern char *xferbuf; + extern size_t xferbufsize; + + /* Make sure we use a multiple of BUFSIZ for efficiency. */ + xferbufsize = (MAX_XFER_BUFSIZE / BUFSIZ) * BUFSIZ; + while (1) { + xferbuf = (char *) malloc (xferbufsize); + if (xferbuf != NULL || xferbufsize < 1024) + break; + xferbufsize >>= 2; + } + + if (xferbuf != NULL) return; + fatal("out of memory for transfer buffer."); +} /* init_transfer_buffer */ + + + + +void init_prompt(void) +{ + register char *cp; + + percent_flags = at_flags = 0; + for (cp = prompt; *cp; cp++) { + if (*cp == '%') percent_flags = 1; + else if (*cp == '@') at_flags = 1; + } +} /* init_prompt */ + + + +/*ARGSUSED*/ +void lostpeer SIG_PARAMS +{ + if (connected) { + close_streams(1); + if (data >= 0) { + (void) shutdown(data, 1+1); + (void) close(data); + data = -1; + } + connected = 0; + } + if (connected) { + close_streams(1); + connected = 0; + } + hostname[0] = cwd[0] = 0; + logged_in = macnum = 0; +} /* lostpeer */ + + +/* + * Command parser. + */ +void cmdscanner(int top) +{ + register struct cmd *c; + + if (!top) + (void) putchar('\n'); + for (;;) { + if (!doing_script && !UserLoggedIn()) + (void) quit(0, NULL); + if (Gets(strprompt(), line, (size_t)CMDLINELEN) == NULL) { + (void) quit(0, NULL); /* control-d */ + } + eventnumber++; + dbprintf("\"%s\"\n", line); + (void) makeargv(); + if (margc == 0) { + continue; /* blank line... */ + } + c = getcmd(margv[0]); + if (c == (struct cmd *) -1) { + (void) printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + if (!implicit_cd(margv[0])) + (void) printf("?Invalid command\n"); + continue; + } + if (c->c_conn && !connected) { + (void) printf ("Not connected.\n"); + continue; + } + if ((*c->c_handler)(margc, margv) == USAGE) + cmd_usage(c); + if (c->c_handler != help) + break; + } + (void) Signal(SIGINT, intr); + (void) Signal(SIGPIPE, lostpeer); +} /* cmdscanner */ + + + + +char *strprompt(void) +{ + time_t tyme; + char eventstr[8]; + char *dname, *lastlinestart; + register char *p, *q; + string str; + int flag; + + if (at_flags == 0 && percent_flags == 0) { + epromptlen = strlen(prompt); + return (prompt); /* But don't overwrite it! */ + } + epromptlen = 0; + lastlinestart = prompt2; + if (at_flags) { + for (p = prompt, q = prompt2, *q = 0; (*p); p++) { + if (*p == '@') switch (flag = *++p) { + case '\0': + --p; + break; + case 'M': + if (CheckNewMail() > 0) + q = Strpcpy(q, "(Mail) "); + break; + case 'N': + q = Strpcpy(q, "\n"); + lastlinestart = q; + epromptlen = 0; + break; + case 'P': /* reset to no bold, no uline, no inverse, etc. */ + if (ansi_escapes) { + q = Strpcpy(q, tcap_normal); + epromptlen += tcl_normal; + } + break; + case 'B': /* toggle boldface */ + if (ansi_escapes) { + q = Strpcpy(q, tcap_boldface); + epromptlen += tcl_bold; + } + break; + case 'U': /* toggle underline */ + if (ansi_escapes) { + q = Strpcpy(q, tcap_underline); + epromptlen += tcl_uline; + } + break; + case 'R': + case 'I': /* toggle inverse (reverse) video */ + if (ansi_escapes) { + q = Strpcpy(q, tcap_reverse); + epromptlen += tcl_rev; + } + break; + case 'D': /* insert current directory */ + case 'J': + if ((flag == 'J') && (remote_is_unix)) { + /* Not the whole path, just the dir name. */ + dname = rindex(cwd, '/'); + if (dname == NULL) + dname = cwd; + else if ((dname != cwd) && (dname[1])) + ++dname; + } else + dname = cwd; + if (dname[0]) { + q = Strpcpy(q, dname); + q = Strpcpy(q, " "); + } + break; + case 'H': /* insert name of connected host */ + if (logged_in) { + (void) sprintf(str, "%s ", hostname); + q = Strpcpy(q, str); + } + break; + case 'C': /* Insert host:path (colon-mode format. */ + if (logged_in) { + (void) sprintf(str, "%s:%s ", hostname, cwd); + q = Strpcpy(q, str); + } else + q = Strpcpy(q, "(not connected)"); + break; + case 'c': + if (logged_in) { + (void) sprintf(str, "%s:%s\n", hostname, cwd); + q = Strpcpy(q, str); + lastlinestart = q; /* there is a \n at the end. */ + epromptlen = 0; + } + break; + case '!': + case 'E': /* insert event number */ + (void) sprintf(eventstr, "%ld", eventnumber); + q = Strpcpy(q, eventstr); + break; + default: + *q++ = *p; /* just copy it; unknown switch */ + } else + *q++ = *p; + } + *q = '\0'; + } else + (void) strcpy(prompt2, prompt); + +#ifndef NO_STRFTIME + if (percent_flags) { + /* only strftime if the user requested it (with a %something), + otherwise don't waste time doing nothing. */ + (void) time(&tyme); + (void) Strncpy(str, prompt2); + (void) strftime(prompt2, sizeof(str), str, localtime(&tyme)); + } +#endif + epromptlen = (size_t) ((long) strlen(lastlinestart) - (long) epromptlen); + return (prompt2); +} /* strprompt */ + + +/* + * Slice a string up into argc/argv. + */ + +void makeargv(void) +{ + char **argp; + + margc = 0; + argp = margv; + stringbase = line; /* scan from first of buffer */ + argbase = argbuf; /* store from first of buffer */ + slrflag = 0; + while ((*argp++ = slurpstring()) != 0) + margc++; +} /* makeargv */ + + + + +/* + * Parse string into argbuf; + * implemented with FSM to + * handle quoting and strings + */ +char *slurpstring(void) +{ + int got_one = 0; + register char *sb = stringbase; + register char *ap = argbase; + char *tmp = argbase; /* will return this if token found */ + + if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ + switch (slrflag) { /* and $ as token for macro invoke */ + case 0: + slrflag++; + stringbase++; + return ((*sb == '!') ? "!" : "$"); + /* NOTREACHED */ + case 1: + slrflag++; + altarg = stringbase; + break; + default: + break; + } + } + +S0: + switch (*sb) { + + case '\0': + goto OUT; + + case ' ': + case '\t': + case '\n': + case '=': + sb++; goto S0; + + default: + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = sb; + break; + default: + break; + } + goto S1; + } + +S1: + switch (*sb) { + + case ' ': + case '\t': + case '\n': + case '=': + case '\0': + goto OUT; /* end of token */ + + case '\\': + sb++; goto S2; /* slurp next character */ + + case '"': + sb++; goto S3; /* slurp quoted string */ + + default: + *ap++ = *sb++; /* add character to token */ + got_one = 1; + goto S1; + } + +S2: + switch (*sb) { + + case '\0': + goto OUT; + + default: + *ap++ = *sb++; + got_one = 1; + goto S1; + } + +S3: + switch (*sb) { + + case '\0': + goto OUT; + + case '"': + sb++; goto S1; + + default: + *ap++ = *sb++; + got_one = 1; + goto S3; + } + +OUT: + if (got_one) + *ap++ = '\0'; + argbase = ap; /* update storage pointer */ + stringbase = sb; /* update scan pointer */ + if (got_one) { + return(tmp); + } + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = (char *) 0; + break; + default: + break; + } + return((char *)0); +} /* slurpstring */ + +/* + * Help command. + * Call each command handler with argc == 0 and argv[0] == name. + */ +int +help(int argc, char **argv) +{ + register struct cmd *c; + int showall = 0, helpall = 0; + char *arg; + int i, j, k; + int nRows, nCols; + int nCmds2Print; + int screenColumns; + int len, widestName; + char *cp, **cmdnames, spec[16]; + + if (argc == 2) { + showall = (strcmp(argv[1], "showall") == 0); + helpall = (strcmp(argv[1], "helpall") == 0); + } + if (argc == 1 || showall) { + (void) printf("\ +Commands may be abbreviated. 'help showall' shows aliases, invisible and\n\ +unsupported commands. 'help <command>' gives a brief description of <command>.\n\n"); + + for (c = cmdtab, nCmds2Print=0; c->c_name != NULL; c++) + if (!c->c_hidden || showall) + nCmds2Print++; + + if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL) + fatal("out of memory!"); + + for (c = cmdtab, i=0, widestName=0; c->c_name != NULL; c++) { + if (!c->c_hidden || showall) { + cmdnames[i++] = c->c_name; + len = (int) strlen(c->c_name); + if (len > widestName) + widestName = len; + } + } + + if ((cp = getenv("COLUMNS")) == NULL) + screenColumns = 80; + else + screenColumns = atoi(cp); + + widestName += 2; /* leave room for white-space in between cols. */ + nCols = screenColumns / widestName; + /* if ((screenColumns % widestName) > 0) nCols++; */ + nRows = nCmds2Print / nCols; + if ((nCmds2Print % nCols) > 0) + nRows++; + + (void) sprintf(spec, "%%-%ds", widestName); + for (i=0; i<nRows; i++) { + for (j=0; j<nCols; j++) { + k = nRows*j + i; + if (k < nCmds2Print) + (void) printf(spec, cmdnames[k]); + } + (void) printf("\n"); + } + Free(cmdnames); + } else if (helpall) { + /* Really intended to debug the help strings. */ + for (c = cmdtab; c->c_name != NULL; c++) { + cmd_help(c); + cmd_usage(c); + } + } else while (--argc > 0) { + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + (void) printf("?Ambiguous help command %s\n", arg); + else if (c == (struct cmd *)0) + (void) printf("?Invalid help command %s\n", arg); + else { + cmd_help(c); + cmd_usage(c); + } + } + return NOERR; +} /* help */ + + +/* + * If the user wants to, s/he can specify the maximum size of the log + * file, so it doesn't waste too much disk space. If the log is too + * fat, trim the older lines (at the top) until we're under the limit. + */ +void trim_log(void) +{ + FILE *new, *old; + struct stat st; + long fat; + string tmplogname, str; + + if (logsize <= 0 || *logfname == 0 || stat(logfname, &st) || + (old = fopen(logfname, "r")) == NULL) + return; /* never trim, or no log */ + fat = st.st_size - logsize; + if (fat <= 0L) return; /* log too small yet */ + while (fat > 0L) { + if (FGets(str, old) == NULL) return; + fat -= (long) strlen(str); + } + /* skip lines until a new site was opened */ + while (1) { + if (FGets(str, old) == NULL) { + (void) fclose(old); + (void) unlink(logfname); + return; /* nothing left, start anew */ + } + if (*str != '\t') break; + } + + /* copy the remaining lines in "old" to "new" */ + (void) Strncpy(tmplogname, logfname); + tmplogname[strlen(tmplogname) - 1] = 'T'; + if ((new = fopen(tmplogname, "w")) == NULL) { + (void) PERROR("trim_log", tmplogname); + return; + } + (void) fputs(str, new); + while (FGets(str, old)) + (void) fputs(str, new); + (void) fclose(old); (void) fclose(new); + if (unlink(logfname) < 0) + PERROR("trim_log", logfname); + if (rename(tmplogname, logfname) < 0) + PERROR("trim_log", tmplogname); +} /* trim_log */ + + + + +int CheckNewMail(void) +{ + struct stat stbuf; + + if (*mail_path == '\0') return 0; + if (stat(mail_path, &stbuf) < 0) { /* cant find mail_path so we'll */ + *mail_path = '\0'; /* never check it again */ + return 0; + } + + /* + * Check if the size is non-zero and the access time is less than + * the modify time -- this indicates unread mail. + */ + if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) { + if (stbuf.st_mtime > mbox_time) { + (void) printf("%s\n", NEWMAILMESSAGE); + mbox_time = stbuf.st_mtime; + } + return 1; + } + + return 0; +} /* CheckNewMail */ + + + +#ifdef CURSES +int termcap_get(char **dest, char *attr) +{ + static char area[1024]; + static char *s = area; + char *buf, *cp; + int i, result = -1; + int len = 0; + + *dest = NULL; + while (*attr != '\0') { + buf = tgetstr(attr, &s); + if (buf != NULL && buf[0] != '\0') { + for (i = 0; (buf[i] <= '9') && (buf[i] >= '0'); ) + i++; + /* Get rid of the terminal delays, like "$<2>". */ + if ((cp = strstr(&(buf[i]), "$<")) != NULL) + *cp = 0; + if (*dest == NULL) + *dest = (char *)malloc(strlen(&(buf[i])) + 1); + else + *dest = (char *)realloc(*dest, len + strlen(&(buf[i])) + 1); + if (*dest == NULL) + break; + (void) strcpy(*dest + len, &(buf[i])); + len += strlen (&(buf[i])); + } + attr += 2; + } + if (*dest == NULL) + *dest = ""; + else + result = 0; + return (result); +} /* termcap_get */ + + + +void termcap_init(void) +{ + char *term; + + if ((term = getenv("TERM")) == NULL) { + term = "dumb"; /* TAR */ + ansi_escapes = 0; + } + if (tgetent(tcbuf,term) != 1) { + (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term); + } else { + (void) termcap_get(&tcap_normal, "meuese"); + if (termcap_get(&tcap_boldface, "md") < 0) { + /* Dim-mode is better than nothing... */ + (void) termcap_get(&tcap_boldface, "mh"); + } + (void) termcap_get(&tcap_underline, "us"); + (void) termcap_get(&tcap_reverse, "so"); + tcl_normal = strlen(tcap_normal); + tcl_bold = strlen(tcap_boldface); + tcl_uline = strlen(tcap_underline); + tcl_rev = strlen(tcap_reverse); + } + +} /* termcap_init */ + + + +static int c_output(int c) +{ + return (putchar(c)); +} /* c_output */ + + + + +void tcap_put(char *cap) +{ + tputs(cap, 0, c_output); +} /* tcap_put */ + +#endif /* CURSES */ + +/* eof main.c */ + diff --git a/usr.bin/ncftp/main.h b/usr.bin/ncftp/main.h new file mode 100644 index 000000000000..1891b61d77c6 --- /dev/null +++ b/usr.bin/ncftp/main.h @@ -0,0 +1,50 @@ +/* main.h */ + +#ifndef _main_h_ +#define _main_h_ + +/* $RCSfile: main.h,v $ + * $Revision: 14020.12 $ + * $Date: 93/05/21 05:45:33 $ + */ + +struct userinfo { + str32 username; + string homedir; + string shell; + string hostname; + int uid; +}; + +void intr SIG_PARAMS; +int getuserinfo(void); +int init_arrays(void); +void init_transfer_buffer(void); +void init_prompt(void); +void lostpeer SIG_PARAMS; +void cmdscanner(int top); +char *strprompt(void); +void makeargv(void); +char *slurpstring(void); +int help(int argc, char **argv); +void trim_log(void); +int CheckNewMail(void); + +#ifdef CURSES + void tcap_put(char *cap); + void termcap_init(void); + int termcap_get(char **dest, char *attr); +# ifndef TERMH /* <term.h> would take care of this. */ +# ifdef NO_CONST + extern char *tgetstr(char *, char **); +# else + extern char *tgetstr(const char *, char **); +# endif +# endif /* TERMH */ +#endif /* CURSES */ + +/* Should be in a 'tips.h,' but... */ +void PrintTip(void); + +#endif /* _main_h_ */ + diff --git a/usr.bin/ncftp/ncftp.1 b/usr.bin/ncftp/ncftp.1 new file mode 100644 index 000000000000..37cf474748e2 --- /dev/null +++ b/usr.bin/ncftp/ncftp.1 @@ -0,0 +1,1393 @@ +.\"------- +.\" Man page portability notes +.\" +.\" These are some notes on conventions to maintain for greatest +.\" portability of this man page to various other versions of +.\" nroff. +.\" +.\" When you want a \ to appear in the output, use \e in the man page. +.\" (NOTE this comes up in the rc grammar, where to print out '\n' the +.\" man page must contain '\en'.) +.\" +.\" Evidently not all versions of nroff allow the omission of the +.\" terminal " on a macro argument. Thus what could be written +.\" +.\" .Cr "exec >[2] err.out +.\" +.\" in true nroffs must be written +.\" +.\" .Cr "exec >[2] err.out" +.\" +.\" instead. +.\" +.\" Use symbolic font names (e.g. R, I, B) instead of the standard +.\" font positions 1, 2, 3. Note that for Xf to work the standard +.\" font names must be single characters. +.\" +.\" Note that sentences should end at the end of a line. nroff and +.\" troff will supply the correct intersentence spacing, but only if +.\" the sentences end at the end of a line. Explicit spaces, if given, +.\" are apparently honored and the normal intersentence spacing is +.\" supressed. +.\" +.\" DaviD W. Sanderson +.\"------- +.\" Dd distance to space vertically before a "display" +.\" These are what n/troff use for interparagraph distance +.\"------- +.if t .nr Dd .4v +.if n .nr Dd 1v +.\"------- +.\" Sp space down the interparagraph distance +.\"------- +.de Sp +.sp \\n(Ddu +.. +.\"------- +.\" Ds begin a display, indented .5 inches from the surrounding text. +.\" +.\" Note that uses of Ds and De may NOT be nested. +.\"------- +.de Ds +.Sp +.in +0.5i +.nf +.. +.\"------- +.\" De end a display (no trailing vertical spacing) +.\"------- +.de De +.fi +.in +.. +.TH NcFTP 1 "" NCEMRSoft +.\"------- +.SH "NAME" +.\"------- +NcFTP \(em Internet file transfer program +.\"------- +.SH "SYNOPSIS" +.\"------- +.B ncftp +.RI [ "program options" ] +.RI [[ "open options" ] +.IR hostname [\c +.B :\c +.IR pathname ]] +.\"------- +.SH "DESCRIPTION" +.\"------- +.I NcFTP +is a user interface to the Internet standard +.IR "File Transfer Protocol" . +This program allows a user to transfer files to and from a remote network +site, and offers additional features that are not found in the standard +interface, +.IR ftp . +.\"------- +.SH "FEATURES" +.\"------- +Program options will be explained later in this document. +Let's get down to business and go over the features +that make this program worthwhile. +.PP +Here is the list of section headers; I have my $MANPAGER environment +variable set to use +.RB `` "less \-i" '' +so that I can skip to the section I +want (otherwise, +.BI / regex +commands to the pager won't match the section +headers because of the formatting codes; +the +.RB `` \-i '' +can search through the formatting codes) +.Ds +Establishing the remote connection +Format of the RC file +The Recent-sites file +Redialing a busy remote site +Supplying a sitename from your shell's command line +Using Colon-mode +Using FTP-cat and FTP-more mode +Supplying a port number with the open command +Displaying and changing program variables +Program variables +Listing a remote directory +Viewing a remote directory with your pager +Redisplaying the last directory listing +Fetching files from the remote host +Viewing a remote file with your pager +Creating a message file on the remote host +Looking up site names and addresses +Checking the configuration of the program +Using the command shell +Customizing the prompt +Keeping a log of your file transfers +Program options +A sample RC file +.De +.\"------- +.SH "Establishing the remote connection" +.\"------- +Just opening a connection to a remote server was inconvenient enough in the +stock +.I ftp +program to justify writing this program. +Here at +.IR NCEMRSoft , +we want to do our business as quickly and painlessly as possible. +We'd +rather save time and wear and tear on our metacarpals than bother typing +entire site names, usernames, and email addresses masquerading as passwords, +and setting binary mode. +.PP +We made all connections anonymous by default, and we automatically send our +email address for the password on those connections. +We allowed for site +names to be abbreviated. +.PP +For each commonly accessed site, you can put an entry in your program +preferences file (let's call it the ``ncftprc file'' or ``RC file'' for short). +To open the site, from the command shell all you do is type: +.Ds +open wuarchive.wustl.edu +.De +.PP +or +.Ds +o wuarchive.wustl.edu +.De +.PP +As promised, you can abbreviate that further. +Just use any abbreviation that +would match only the site you had in mind. +For the previous example, you +could try: +.Ds +o wuarc +o wustl +o stl +o wu +.De +.PP +Any of those abbreviations would open wuarchive.wustl.edu anonymously, +sending your anon-password (usually set to your email address) as the +password. +Keep in mind that the program tries opening the first site +that matches the abbreviation you supplied. +So: +.Ds +o w +.De +.PP +might match a site named bowser.nintendo.co.jp if that site appeared before +your entry for wuarchive.wustl.edu. +.PP +Most of the time we open remote sites anonymously, but +there are times where you need to specifically open a site with an actual +username and password. +Let's say my partner, Phil Dietz, wants to FTP +something out of my account. +Perhaps he wants to fetch the latest version +of the source code to +.I NcFTP +so he can optimize something or add a new feature behind my back. +Since the +program opens remote sites anonymously by default (actually, you can change +this behavior; more on that later), he would have to specify a flag to the +.I open +command so he can supply my username and password. +He would try: +.Ds +o \-u sphygmomanometer.unl.edu +.De +.PP +or, more likely: +.Ds +o \-u sph +.De +.PP +Then the program would prompt him for a username (login, whatever) and a +password: +.Ds +Login Name (pdietz): mgleason +Password: ******** +.De +.PP +If he got it right, he could raid my stuff. +If not, he'd probably drop +me an email asking me to quit changing my password so often. +.PP +There are even times where you want to FTP from your own account, like if +you are debugging an FTP client you wrote. +At this prompt: +.Ds +Login Name (mgleason): +.De +.PP +I could just hit return to tell the program that I want ``mgleason'' as my +username, then I would enter my password. +.\"------- +.SH "Format of the RC file" +.\"------- +This release of the program is somewhat compatible with the stock +.I ftp +program's +.B ".netrc" +file. +However, I can promise you that in the near future the program will +use a new format, so don't invest too much time in it. +.PP +The RC file can be named +.RB `` ncftprc '', +.RB `` netrc '', +or +.RB `` .ncftprc '', +but it is usually named +.RB `` .netrc '' +so it can be used with the stock +.I ftp +program. +.I NcFTP +looks in the current working directory for any of those files, and then in +your home directory, and after that it gives up (which is OK, because RC +files aren't mandatory). +.PP +The file usually starts with +.I #set +and +.I #unset +commands that do things +to the programs variables. +The reason for the ``#'' is so the stock +.I ftp +program will think they are comments. +You might have this appearing as +the first few lines in your RC file (I'll explain later): +.Ds +#set debug 1 +#set pager "less \-EMi" +#unset startup\-msg +.De +.PP +After those, you put in machine entries for each of your favorite sites. +Let's put in an entry for wuarchive.wustl.edu. +First you would put: +.Ds +machine wuarchive.wustl.edu +.De +.PP +Then you could put in your username, password, and account if you like: +.Ds +user anonymous +password \-mgleason@cse.unl.edu +account wuarc.does.not.use.accounts +.De +.PP +Following that, you would add the startup macro that is run +each time you connect to wuarchive. +You must start it with this line: +.Ds +macdef init +.De +.PP +Then put in the commands you want to do: +.Ds +cd /graphics/gif +ls \-lt +.De +.PP +After that, you end the macro with a blank line (important!). +The finished machine entry would look like the following. +To make the transition to the impending new format less painful, +I recommend you adhere to this format: +.ta 6m +6m +.Ds +machine wuarchive.wustl.edu + user anonymous + password \-mgleason@cse.unl.edu + account wuarc.does.not.use.accounts + macdef init + cd /graphics/gif + ls \-lt +.RI \t( "mandatory blank line to end the macro" ) +.De +.PP +Of course, if all you want to do is open wuarchive anonymously, you +needn't bother with the ``user'', ``password'', and ``account'' lines. +You may want to put them in if you plan on using the stock +.I ftp +program, though. +Try something like this: +.ta 6m +6m +.Ds +machine wuarchive.wustl.edu + macdef init + cd /graphics/gif + ls \-lt +.RI \t( "mandatory blank line to end the macro" ) +.De +.PP +You can tell the program to not run the startup macro if you supply +.B "\-i" +to the +.I open +command. +.PP +Really, you should only bother adding entries for sites that you want to +run startup macros upon connection. +The next section explains why. +.\"------- +.SH "The Recent-sites file" +.\"------- +Each time you open a site, the program saves the name of the site and the +last directory you were in to the +.I recent-sites file +which is named +.B ".ncrecent" +and placed in your home directory. +The program saves a +predetermined number of these sites in the file, and when it reaches the +limit, it discards the oldest entry so it can add a new one. +.PP +You can just go ahead and use the name of the site you want with the +.I open +command if you know it is in the +.I recent\-file +(and you can abbreviate the +name, just like those in the RC file). +But if you cannot remember what the +name of the site you want, all you do is run the +.I open +command with +no site parameter: +.Ds +open +.De +.PP +This will pop up a list of the sites in the +.IR "recent-file" , +and sites in your RC file. +At the open prompt, just type the name (or an +abbreviation of that name) or the number preceding the site name to open +that site. +After opening the site you wanted, the program sets the remote +working directory to the same one you left in the last time you called. +.PP +If you don't like the idea of having the sites you called stored on disk, +you can turn this feature off using an +.I unset +command, explained later. +.\"------- +.SH "Redialing a busy remote site" +.\"------- +Some remote sites limit the number of leeches, er, anonymous connections +at a time to reduce the load on the host computer. +You can use the +.I open +command's redial feature to keep attempting connections until you get on, +although that is not a very polite thing to do. +The simplest way to do +this would be to just supply the +.B \-r +option: +.Ds +open \-r wuarc +.De +.PP +There are also options you can use to tweak redial. +The +.B \-d +flag sets +the delay between dials, and the +.B \-g +flag sets a limit on how many dials +should be attempting before giving up. +If you don't supply +.B \-g +the program will dial a day and forever (which my Number Theory professor, +Dr. Mientka, says is longer than forever and a day) +until it connects successfully, or until you get sick of waiting and hit the +interrupt key (usually ^C). +.PP +This example dials wuarchive every ten minutes, giving up after twenty +attempts. +Note that the redial delay is specified in seconds: +.Ds +open \-r \-d 600 \-g 20 wuarc +.De +.PP +Please be considerate when you use redialing, so you won't tax the network. +Site administrators can and do get angry when they get flooded with +connections. +.\"------- +.SH "Supplying a sitename from your shell's command line" +.\"------- +When you run the program: +.Ds +ncftp +.De +.PP +by itself does nothing and waits for you to type commands to the program's +own shell. +Just like the stock +.I ftp +program, you can supply a site name +on the command line: +.Ds +ncftp wuarchive.wustl.edu +.De +.PP +You can also use abbreviations as usual: +.Ds +ncftp wuarc +.De +.PP +This is equivalent to running the program, then issuing an +.I open +command to open wuarchive. +.\"------- +.SH "Using Colon-mode" +.\"------- +The +.I open +command is not a one-trick pony. +Another option is what I call +.IR "colon-mode" . +This feature is used (most of the time) from your shell's +command line. +.PP +In ancient times, way back during the Disco era, you could use a program +called +.I tftp +to fetch a file using the Internet standard +.I Trivial File Transfer Protocol. +You could use that program to do something like this +from within its shell: +.Ds +get wuarchive.wustl.edu:/graphics/gif/README +.De +.PP +and that would call wuarchive and fetch the +.B README +file. +.PP +You can use this program to do the same thing from your shell's command +line: +.Ds +csh> ncftp wuarchive.wustl.edu:/graphics/gif/README +csh> head README +.De +.PP +This tells your shell, in this case the ``c-shell'' to run +.IR NcFTP , +which +would open wuarchive, fetch +.B /graphics/gif/README +and write the file +.B ./README +in the current working directory, and then exits. +This is nice if you don't +want to browse around the remote site, and you know exactly want you want. +It would also come in handy in shell scripts, where you don't want to +enter the command shell, and might not want the program to spew output. +.PP +You can use +.I colon-mode +to set the starting remote working directory also: +.Ds +csh> ncftp wuarchive.wustl.edu:/graphics/gif +.De +.PP +This would run the program, open wuarchive, and +.I cd +to the gif directory, then run the program's command shell so you can +browse. +.PP +.I Colon-mode +is also available from within the program's command shell. +At a prompt you can do stuff like this: +.Ds +ncftp> open wuarchive.wustl.edu:/graphics/gif/README +ncftp> o wuarc:/graphics/gif +.De +.\"------- +.SH "Using FTP-cat and FTP-more mode" +.\"------- +There are times where you might not want the program to write a +.I colon-mode +file in the current working directory, or perhaps you want to pipe the +output of a remote file into something else. +.I Colon-mode +has options to +do this. +It was inspired by the guy who wrote the +.I ftpcat +perl script. +The +.B \-c +option tells the program to write on the standard +output stream. +The +.B \-m +option pipes the file into your pager (like +.IR more ")" +Of course this won't work if the thing you give +.I colon-mode +is a directory! This example just dumps a remote file to stdout: +.Ds +csh> ncftp \-c wuarc:/graphics/gif/README +\&... +csh> +.De +.PP +This example redirects a remote file into a different +location: +.Ds +csh> ncftp \-c wu:/README > ~pdietz/thesis.tex +.De +.PP +This one shows how to use a pipeline: +.Ds +csh> ncftp \-c wuarc:/README | tail | wc \-l +10 +csh> +.De +.PP +This shows how to page a remote file: +.Ds +csh> ncftp \-m wuarc:/graphics/gif/README +\&... +csh> +.De +.\"------- +.SH "Supplying a port number with the open command" +.\"------- +This option just didn't fit anywhere else, so to finish out the open command, +.B \-p +lets you supply a port number if you have to +.I ftp +to a site using an nonstandard port number. +Personally, I have yet to use this feature, but it is +there for compatibility with the stock +.I ftp +program. +.\"------- +.SH "Displaying and changing program variables" +.\"------- +Now I'll explain the commands unique to +.IR NcFTP . +The others should perform the +same as they would in the stock +.I ftp +program; +consult the man page for it if you want those explained, +or use the +.I help +command for a brief blurb. +.PP +The +.I show +command is used to display program variables and their values. +.Ds +show all +.De +.PP +or +.Ds +show +.De +.PP +would display all the variables with their values. +.Ds +.RI show " var1 var2 ... varN" +.De +.PP +would display each specified variable and its value. +.PP +The +.I set +command changes the value of a program variable. +Its syntax is: +.Ds +.RI set " varname value" +.De +.PP +For Boolean or Integer variables, +.Ds +.RI set " varname" +.De +.PP +would set the value of the variable +.I varname +to +.B 1 +.RB ( true ). +.PP +The +.I unset +command can be used to set the variable to its default value, +or for Boolean and Integer variables, set the value of the variable to +.B 0 +.RB ( false ). +For String variables, you can use this to set the value to an +empty string. +.PP +You can use any of those three commands in both the command shell, +or in the RC file with a ``#'' prepended. +.\"------- +.SH "Program variables" +.\"------- +Each variable can be one of the following types: +.TP +Boolean: +Can be +.RB `` on '' +or +.RB `` off '' +(you can also use +.RB `` 1 '' +or +.RB `` 0 ''). +.TP +Integer: +Can be any positive or negative number, or +.BR 0 . +.TP +String: +Is a string of characters. +If the string needs to have a space +in it, make sure you surround the whole string with double quotes in a +.I set +command. +.PP +Variables follow. +Some variables are explained later in the relevant sections. +.TP +.IR anon\-open " (Boolean)" +Tells whether the default login mode is anonymous if +on, or if off, will prompt for a username/password. +You can always override this by using either +.B \-a +or +.B \-u +with the +.I open +command. +.TP +.IR anon\-password " (String)" +Sends this as the password when you login anonymously. +By default this is your email address. +.TP +.IR ansi\-escapes " (Boolean)" +If on, the program can use boldface, underline, +and inverse text. +.TP +.IR auto\-binary " (Boolean)" +If on, sets the transfer type to binary mode +immediately after connection. +.TP +.IR debug " (Integer)" +Sets the debugging level. +.TP +.IR gateway\-login " (String)" +Tells which username to use when logging in to +your firewall gateway host. +.TP +.IR gateway\-host " (String)" +The site which is acting as your firewall gateway, +or empty if you aren't using one. +.TP +.IR local\-dir " (String)" +The current local working directory. +I like to set this from my RC file, +so all my files go into my download directory. +.TP +.IR logfile " (String)" +The name of your personal transfer log, or empty +if you aren't using a transfer log. +.TP +.IR logsize " (Integer)" +The maximum ceiling of your log file, before the program +removes old entries. +.TP +.IR mprompt " (Boolean)" +If on, prompts for each remote file expanded from a +wildcard globbing expression. +.TP +.IR netrc " (String, Read-only)" +Tells you the name of the RC file in use. +.TP +.IR pager " (String)" +The pathname and flags of the program used to display +output one screenful at a time. +The default is the value of your $PAGER +environment variable. +.TP +.IR prompt " (String)" +The prompt specification that expands into the prompt. +.TP +.IR progress\-reports " (Integer)" +Which progress meter to use, or +.B 0 +if you don't want progress reports during file transfers. +Set it to +.B 1 +for a simple percentage meter; +.B 2 +for a fancy bar graph indicator; +.B 3 +to print just the number of kilobytes transferred; or +.B 4 +to print one dot for each 10% transferred, if you +want to avoid the use of backspaces. Note that the program +may use a different meter depending on how cooperative the +remote host is, and what you have the +.I ansi\-escapes +variable set to. +.TP +.IR recent\-list " (Boolean)" +If on, uses and updates the +.I recent\-file. +.TP +.IR remote\-is\-unix " (Boolean)" +Set automatically by the program upon connection, +you may need to use this in a startup macro if the program guessed +that a remote site was UNIX when it really is not. +.TP +.IR startup\-msg " (Boolean)" +If on, prints the opening message and tip. +.TP +.IR tips " (Boolean)" +If on, prints a tip on how to use the program better each +time you run the program. +.TP +.IR type " (String)" +The name of the file transfer mode in use, +such as +.RB `` binary '' +or +.RB `` ascii ''. +.TP +.IR verbose " (String/Integer)" +Controls the amount of output spewed by the program. +You can supply either the first character of the name of the +verbosity level, or its number: +.RS +.TP +.IR "Q" "uiet (\-1)" +Won't print any output at all, even if an error occurs. +.TP +.IR "E" "rrors Only (0)" +No output, except when errors occur. +.TP +.IR "T" "erse (1)" +Prints errors, and useful output from the remote host. +.TP +.IR "V" "erbose (2)" +Prints everything, even junk output from the remote end. +.RE +.\"------- +.SH "Listing a remote directory" +.\"------- +The +.I ls +and +.I dir +commands perform in a similar manner to those of the +stock +.I ftp +program. +.PP +The +.I ls +command sends the FTP command ``NLST'' for you. +This command has been set so that it defaults +to always listing files in columns (this is the +.B \-C +option given to the UNIX +.I ls +command) and appending +metacharacters to each item name (this is the +.B \-F +option), so you can +see which items are directories, files, links, etcetera. +If you don't want +your items columnized, you can try using the +.B \-1 +option with +.I ls +to print one item per line. +.PP +The +.I dir +command sends the FTP command ``LIST'' for you, which instead +of printing just item names, it prints item sizes, owners, dates, and +permissions as well. +This command is equivalent to +.RB `` "ls \-l" '' +on most remote systems. +.PP +The usage for both commands is the same. +Here is the one for +.IR ls : +.PP +.RS +.B ls +.RI [ \-flags ] +.RI [ "directory and file names" ] +.RI [ redirection ] +.RE +.PP +Note that in this program, you can supply both flags and items to list in +the same command. +The stock version of +.I ftp +doesn't let you do this: +.Ds +ls \-lrt /info\-mac/help +.De +.PP +Another thing that the program does which the others should have done is +let you supply more than one item: +.Ds +ls \-lrt /info\-mac/help /pub /info\-mac/README +.De +.PP +You can also redirect the output into a file, or pipe it into something. +This example shows how to list the contents of the current remote directory, +and save the output into a file in the current local directory: +.Ds +ls \-t >ls.out +.De +.PP +Note that for this to work, there must be no whitespace between the ``>'' +and the filename, unlike your shell command line which allows for extra +whitespace. +This will be (actually, is) fixed in a future version of the +program. +.PP +These examples show how to use a pipe: +.Ds +ls \-t |tail +dir \-t "|less \-CM" +ls \-t "|tail | wc" +.De +.PP +Like the redirection example, there must be no whitespace between the first +pipe character and the rest of the stuff. +The trick is that it has to +appear as one argument to the commands. +The second and third examples +illustrate the use of double quotes to squeeze extra parameters in. +The second example can be done without all that typing. +See the descriptions of the +.I pdir +and +.I pls +commands below. +.\"------- +.SH "Viewing a remote directory with your pager" +.\"------- +Didn't you hate it when you listed a remote directory, only to have most of +the stuff scrolled off your terminal before you could read it? +The +.I pls +and +.I pdir +commands take care of this for you. +As you might have guessed, +they perform exactly like their regular counterparts, +only you view them with your pager. +The pager to use is controlled by the +.I pager +program variable. +.\"------- +.SH "Redisplaying the last directory listing" +.\"------- +The program saves the listing into a local buffer, +so if you need to see it again (probably forgot about +.IR pdir ) +you can use the +.I redir +and +.I predir +commands for this. +.\"------- +.SH "Fetching files from the remote host" +.\"------- +The +.I get +and +.I mget +retrieve remote files for you. +The usage for +.I get +is: +.Ds +get remote\-file [local\-file or redirection] +.De +.PP +To fetch +.B /pub/README +and write it as a file named +.BR ./junk/readme , +try: +.Ds +get /pub/README ./junk/readme +.De +.PP +To fetch +.B /pub/README +and write it as +.BR ./README , +just do: +.Ds +get /pub/README +.De +.PP +This lets you fetch a file using its whole pathname, and write a copy of +it in the current directory, without having to bother with typing a local +filename. +In the unlikely event that you have write permission to a +directory called +.B /pub +on your local machine, it would write +.RB `` README '' +in that directory. +.PP +Most of the time the file you want will be in the current remote directory, +so you can just do these: +.Ds +get README +get README ./junk/readme +.De +.PP +You can also use a redirection for +.IR get , +just like you can with the +.IR ls ", " dir ", and " redir +commands. +As described earlier, you have +to conform to the format below for this release of the program: +.Ds +get README >/dev/null +get README |head +get README "|head \-8" +get README "|less \-EMi" +.De +.PP +The last example is facilitated by the +.I page +command described later. +.PP +The +.I get +command can also use a wildcard expression in an attempt to +match exactly one remote file. +I call it ``Poor Man's File Completion.'' +If you've done a remote listing, and you decide you want to download a +file by the name of +.RB `` obnoxiouslylongpackagename.tar.Z '', +you can use +``PMFC'' to save some keystrokes. +Choose an expression that will only +match that one file, then use it with +.IR get : +.Ds +get obn*.Z a.tar.Z +.De +.PP +If your pattern was unique, +.I get +will fetch that file only. +If the pattern matched more than one file, the program will bitch and moan. +.PP +The +.I mget +command is used to fetch many files at a time. +The difference between +.I get +and +.I mget +is that +.I get +lets you write only one file, +but you can put it in a different directory, while +.I mget +fetches many files, +always writing them in the current local directory. +This example fetches several remote files at once: +.Ds +mget a.file.Z b.file.Z c.tar d.tar.Z +.De +.PP +The +.I mget +command, and its ugly sisters, +.I mput +and +.I mdelete +let you use wildcard expressions. +I could have done the previous example as: +.Ds +mget *.Z c.tar +.De +.PP +instead. +The ``m'' commands will verify each file, +if you have the program variable +.I mprompt +set. +.\"------- +.SH "Viewing a remote file with your pager" +.\"------- +If you would like to read a file on the remote host without saving a copy +of it on your machine, you can use the +.I page +(or +.I more +if you wish) command: +.Ds +page README +page obn*README +page README.Z +.De +.PP +The second example show that you can use ``PMFC'' like you can for +.IR get. +The third example will work also, because if the program knows how to +decompress the file, it will do so before feeding it to your pager. +As stated earlier, +you can change the program to use to page by setting the program variable +.IR pager. +.\"------- +.SH "Creating a message file on the remote host" +.\"------- +Use the +.I create +an empty file on the remote site. +Sometimes it is necessary to leave a note if you can't get in touch +with the remote site's administrator. +For example if a file is corrupted, you could try: +.Ds +create Foo.tar_is_corrupt +.De +.PP +in hopes that the original uploader will replace it. +.\"------- +.SH "Looking up site names and addresses" +.\"------- +You can use the program's builtin +.RI mini- nslookup +facility. +If you wanted to know the site's IP number, but only knew the name you +could do: +.Ds +lookup cse.unl.edu +.De +.PP +This would spit out IP number for that site, in this case ``129.93.1.12''. +If you needed to know what a site's name was, but only knew the IP number, +try: +.Ds +lookup 129.93.1.12 +.De +.PP +This would spit out the name for that site, in this case ``cse.unl.edu''. +.\"------- +.SH "Checking the configuration of the program" +.\"------- +Use the +.I version +command to print version and compilation information about the program. +This will also tell you which optional features are +compiled into the program, such as logging to the system log and which +command line editor (if any) has been installed. +.PP +The author's email address is listed, and if you need to report something, +send the output of this command along with your message. +.\"------- +.SH "Using the command shell" +.\"------- +Just like the stock +.I ftp +program, you type commands to it until you get +bored and hit either ^D or type the +.I quit +command. +.PP +The program supports links to popular command line editing libraries. +If the person who compiled it went to the effort, you will be able to +edit the command line with arrow keys and other editing commands, and also +scroll up and down in the command line history, usually with the up and +down arrows. +You can check the +.I version +command to see if either +``GETLINE'' or ``READLINE'' are installed. +.\"------- +.SH "Customizing the prompt" +.\"------- +You can set the shell's prompt string to whatever you like. +You can use several metacharacters that expand into something each prompt. +The +.RB `` % '' +flags are passed to +.IR strftime (3), +so you can put the date or time in the prompt formatted as you like it: +.Ds +set prompt "%I:%M ncftp>" +.De +.PP +That would insert the current time in the prompt. +.PP +The +.RB `` @ '' +flags are expanded by the program itself. +Here's the list of them. +.PP +If you have an ANSI-compatible terminal, or you have the program variable +.I ansi\-escapes +set, you can use +.BR @B , +.BR @I , +and +.B @U +to turn on boldface, +inverse, and underline text respectively (otherwise they won't insert +anything). +You can also use +.B @R +to turn on inverse (reverse) text. +.B @P +sets the text back to plain text. +.PP +.B @D +Inserts the full path of the current remote directory. +The +.B @J +flag is similar except it inserts only the directory name. +.PP +.B @H +Inserts the name of the remote host. +.B @C +inserts the host and current +directory path in +.I "colon-mode" +format, such as +``cse.unl.edu:/pub/mgleason'', or ``(not connected)''. +The +.B @c +flag is similar, only it will insert ``cse.unl.edu:/pub/mgleason'' and a +newline if connected, otherwise it prints nothing. +The default prompt uses +this flag to print a two line prompt when connected and a one line prompt +when not connected. +.PP +.BR @E " or " @! +inserts the event number (how many commands you've typed). +.PP +.B @M +inserts ``(Mail)\0'' if mail has arrived since running the program. +.PP +.B @N +inserts a newline character. +.\"------- +.SH "Keeping a log of your file transfers" +.\"------- +You can have the program keep a personal log file. +I find it is useful so I can see where I got a certain file, +or what the name of that site was I called two weeks ago. +.PP +To use a log, add: +.Ds +#set logfile ~/.ftplog +.De +.PP +(or whatever you want to name the log) to your RC file. +I don't want my log growing too large and using up all my disk space, +so I also have: +.Ds +#set logsize 10240 +.De +.PP +in my RC file. +If you set the limit on the maximum log size, the program will +keep the log file at or below that size, discarding old entries. +.PP +Note that this is different from having SYSLOG appear in the +.I version +command's output. +When this is on, your actions are recorded to the system +log, so your system administrator can make sure you aren't doing anything +``bad.'' +.\"------- +.SH "Program options" +.\"------- +Remember that you can treat the command line like an +.I open +command, +so all lowercase options are passed to the +.I open +command, and the +uppercase options are handled by the main program. +The uppercase options +are described below; refer to the +.I open +command for descriptions of its options. +.TP +.BI \-D " x" +sets the debugging level to +.IR x . +.TP +.B \-H +runs the +.I version +command and exits, so you can save the output of +it to use when you need to mail me something. +.TP +.B \-I +toggles the mprompt variable; this is provided for compatibility with +.RB `` "ftp \-i" ''. +.TP +.B \-N +disables reading of the RC file; +this is provided for compatibility with +.RB `` "ftp \-n" ''. +.TP +.BI \-V " x" +sets verbosity to level +.I x +.RB ( \-1 , +.BR 0 , +.BR 1 , +.BR 2 ) +or +.RB ( quiet , +.BR errs , +.BR terse , +.BR verbose ). +See the description of the +.I verbose +program variable for more information. +.PP +Here are some example command lines. +Again, see the description of the +.I open +command (especially +.IR "colon-mode" " and " "FTP\-cat mode" ")" +and all its functions for more information. +.PP +This just enters the +.I NcFTP +command shell: +.Ds +csh> ncftp +.De +.PP +This fetches +.B CONTENTS +and then quits: +.Ds +csh> ncftp cse.unl.edu:/pub/mgleason/CONTENTS +.De +.PP +Some others examples, with open options and main program options mixed in: +.Ds +csh> ncftp \-V quiet \-u ftp.unl.edu +csh> ncftp \-c cse.unl.edu:/pub/mgleason/CONTENTS +csh> ncftp \-D 2 \-r \-d 120 \-g 10 \-N ftp.unl.edu +.De +.\"------- +.SH "A sample RC file" +.\"------- +Here is a sample RC file: +.ta 6m +6m +.Ds +#set logfile ~/.ftplog +#set progress\-reports 2 +#set local\-dir /usr/tmp/zz +#set prompt "@B@E @UNcFTP@P @B@M@D@P \->" +.sp +machine sumex\-aim.stanford.edu + macdef init + cd /info\-mac + get ./help/recent\-files.txt "|grep \-v '.abs' > sumex" + !less sumex + pwd +.sp +# This site is in here just so I can use ``apple'' +# as an abbreviation. +machine ftp.apple.com +.sp +# NcFTP will only ask for your password: +machine cse.unl.edu + login mgleason +.sp +# You can supply a login and a password: +machine fake.machine.unl.edu + login mgleason + password mypass + macdef init + cd ./foo/bar +.sp +# If an antiquated non-UNIX machine doesn't use +# the "SYST" command, you may need to unset +# remote\-is\-unix, if the remote host complains +# about ``ls \-CF''. +machine some.vms.unl.edu + macdef init + unset remote\-is\-unix +.sp +.De +.\"------- +.SH "AUTHORS" +.\"------- +.I NcFTP +was written by Mike Gleason, +.I NCEMRSoft +(mgleason@cse.unl.edu), and based on code by the authors of the +.I ftp +from the BSD 4.3 distribution. +.I NcFTP +is copyrighted 1992, 1993 by NCEMRSoft +and 1985, 1989 by the Regents of California. +.PP +Ideas and some code contributed by Phil Dietz, +.I NCEMRSoft +(pdietz@cse.unl.edu). +Testing and debugging done by Phil and +Kok Hon Yin (hkok@cse.unl.edu). +.PP +Extensive man page formatting work +by DaviD W. Sanderson (dws@ssec.wisc.edu). +.\"------- +.SH "BUGS" +.\"------- +Correct execution of many commands depends upon proper behavior +by the remote server. +.PP +The remote server may drop the connection if you take a long time to +page remote files. +.PP +Termcap padding is not correctly displayed. +.PP +There are no such sites named +.I bowser.nintendo.co.jp +or +.IR sphygmomanometer.unl.edu . +.\"------- +.SH "SEE ALSO" +.\"------- +.IR strftime (3), +.IR ftpd (8), +.IR ftp (1), +.IR nslookup (1), +.IR compress (1), +.IR gzip (1), +.IR zcat (1), +.IR fsp (1), +.IR archie (1), +.IR tftp (1). diff --git a/usr.bin/ncftp/open.c b/usr.bin/ncftp/open.c new file mode 100644 index 000000000000..66733a88ec9c --- /dev/null +++ b/usr.bin/ncftp/open.c @@ -0,0 +1,638 @@ +/* open.c */ + +/* $RCSfile: open.c,v $ + * $Revision: 1.1 $ + * $Date: 93/07/09 11:27:07 $ + */ + +#include "sys.h" + +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/ftp.h> + +#include <errno.h> + +#include "util.h" +#include "open.h" +#include "cmds.h" +#include "ftp.h" +#include "ftprc.h" +#include "main.h" +#include "defaults.h" +#include "copyright.h" + +/* open.c globals */ +int remote_is_unix; /* TRUE if remote host is unix. */ +int auto_binary = dAUTOBINARY; +int anon_open = dANONOPEN; + /* Anonymous logins by default? */ +int connected = 0; /* TRUE if connected to server */ + /* If TRUE, set binary each connection. */ +Hostname hostname; /* Name of current host */ +RemoteSiteInfo gRmtInfo; +#ifdef GATEWAY +string gateway; /* node name of firewall gateway */ +string gate_login; /* login at firewall gateway */ +#endif + +/* open.c externs */ +extern char *reply_string, *line, *Optarg, *margv[]; +extern int Optind, margc, verbose, macnum; +extern long eventnumber; +extern struct servent serv; +extern FILE *cout; +extern string anon_password; + +/* Given a pointer to an OpenOptions (structure containing all variables + * that can be set from the command line), this routine makes sure all + * the variables have valid values by setting them to their defaults. + */ + +void InitOpenOptions(OpenOptions *openopt) +{ + /* How do you want to open a site if neither -a or -u are given? + * anon_open is true (default to anonymous login), unless + * defaults.h was edited to set dANONOPEN to 0 instead. + */ + openopt->openmode = anon_open ? openImplicitAnon : openImplicitUser; + + /* Normally you don't want to ignore the entry in your netrc. */ + openopt->ignore_rc = 0; + + /* Set the default delay if the user specifies redial mode without + * specifying the redial delay. + */ + openopt->redial_delay = dREDIALDELAY; + + /* Normally, you only want to try once. If you specify redial mode, + * this is changed. + */ + openopt->max_dials = 1; + + /* You don't want to cat the file to stdout by default. */ + openopt->ftpcat = NO_FTPCAT; + + /* Setup the port number to try. */ +#ifdef dFTP_PORT + /* If dFTP_PORT is defined, we use a different port number by default + * than the one supplied in the servent structure. + */ + openopt->port = dFTP_PORT; + /* Make sure the correct byte order is supplied! */ + openopt->port = htons(openopt->port); +#else + /* Use the port number supplied by the operating system's servent + * structure. + */ + openopt->port = serv.s_port; +#endif + + /* We are not in colon-mode (yet). */ + openopt->colonmodepath[0] = 0; + + /* Set the hostname to a null string, since there is no default host. */ + openopt->hostname[0] = 0; + + /* Set the opening directory path to a null string. */ + openopt->cdpath[0] = 0; +} /* InitOpenOptions */ + + + + +/* This is responsible for parsing the command line and setting variables + * in the OpenOptions structure according to the user's flags. + */ + +int GetOpenOptions(int argc, char **argv, OpenOptions *openopt) +{ + int opt, www; + char *cp, *hostp, *cpath; + + /* First setup the openopt variables. */ + InitOpenOptions(openopt); + + /* Tell Getopt() that we want to start over with a new command. */ + Getopt_Reset(); + while ((opt = Getopt(argc, argv, "aiup:rd:g:cm")) >= 0) { + switch (opt) { + case 'a': + /* User wants to open anonymously. */ + openopt->openmode = openExplicitAnon; + break; + + case 'u': + /* User wants to open with a login and password. */ + openopt->openmode = openExplicitUser; + break; + + case 'i': + /* User wants to ignore the entry in the netrc. */ + openopt->ignore_rc = 1; + break; + + case 'p': + /* User supplied a port number different from the default + * ftp port. + */ + openopt->port = atoi(Optarg); + if (openopt->port <= 0) { + /* Probably never happen, but just in case. */ + (void) printf("%s: bad port number (%s).\n", argv[0], Optarg); + goto usage; + } + /* Must ensure that the port is in the correct byte order! */ + openopt->port = htons(openopt->port); + break; + + case 'd': + /* User supplied a delay (in seconds) that differs from + * the default. + */ + openopt->redial_delay = atoi(Optarg); + break; + + case 'g': + /* User supplied an upper-bound on the number of redials + * to try. + */ + openopt->max_dials = atoi(Optarg); + break; + + case 'r': + openopt->max_dials = -1; + break; + + case 'm': + /* ftpcat mode is only available from your shell command-line, + * not from the ncftp shell. Do that yourself with 'more zz'. + */ + if (eventnumber == 0L) { + /* If eventnumber is zero, then we were called directly + * from main(), and before the ftp shell has started. + */ + openopt->ftpcat = FTPMORE; + /* ftpcat mode is really ftpmore mode. */ + break; + } else { + fprintf(stderr, +"You can only use this form of colon-mode (-m) from your shell command line.\n\ +Try 'ncftp -m wuarchive.wustl.edu:/README'\n"); + goto usage; + } + /* break; */ + + case 'c': + /* ftpcat mode is only available from your shell command-line, + * not from the ncftp shell. Do that yourself with 'get zz -'. + */ + if (eventnumber == 0L) { + /* If eventnumber is zero, then we were called directly + * from main(), and before the ftp shell has started. + */ + openopt->ftpcat = FTPCAT; + break; + } else { + fprintf(stderr, +"You can only use ftpcat/colon-mode from your shell command line.\n\ +Try 'ncftp -c wuarchive.wustl.edu:/README > file.'\n"); + goto usage; + } + /* break; */ + + default: + usage: + return USAGE; + } + } + + if (argv[Optind] == NULL) { + /* No host was supplied. Print out the list of sites we know + * about and ask the user for one. + */ + PrintSiteList(); + (void) Gets("(site to open) ", openopt->hostname, sizeof(openopt->hostname)); + /* Make sure the user just didn't hit return, in which case we + * just give up and go home. + */ + if (openopt->hostname[0] == 0) + goto usage; + } else { + /* The user gave us a host to open. + * + * First, check to see if they gave us a colon-mode path + * along with the hostname. We also understand a WWW path, + * like "ftp://bang.nta.no/pub/fm2html.v.0.8.4.tar.Z". + */ + hostp = argv[Optind]; + cpath = NULL; + if ((cp = index(hostp, ':')) != NULL) { + *cp++ = '\0'; + cpath = cp; + www = 0; /* Is 0 or 1, depending on the type of path. */ + if ((*cp == '/') && (cp[1] == '/')) { + /* First make sure the path was intended to be used + * with ftp and not one of the other URLs. + */ + if (strcmp(argv[Optind], "ftp")) { + fprintf( + stderr, + "Bad URL '%s' -- WWW paths must be prefixed by 'ftp://'.\n", + argv[Optind] + ); + goto usage; + } + + cp += 2; + hostp = cp; + cpath = NULL; /* It could have been ftp://hostname only. */ + + if ((cp = index(hostp, '/')) != NULL) { + *cp++ = '\0'; + cpath = cp; + } + www = 1; + } + if (cpath != NULL) { + (void) Strncpy(openopt->colonmodepath, www ? "/" : ""); + (void) Strncat(openopt->colonmodepath, cpath); + dbprintf("Colon-Mode Path = '%s'\n", openopt->colonmodepath); + } + } + (void) Strncpy(openopt->hostname, hostp); + dbprintf("Host = '%s'\n", hostp); + } + return NOERR; +} /* GetOpenOptions */ + + + + +/* This examines the format of the string stored in the hostname + * field of the OpenOptions, and sees if has to strip out a colon-mode + * pathname (to store in the colonmodepath field). Since colon-mode + * is run quietly (without any output being generated), we init the + * login_verbosity variable here to quiet if we are running colon-mode. + */ +int CheckForColonMode(OpenOptions *openopt, int *login_verbosity) +{ + /* Usually the user doesn't supply hostname in colon-mode format, + * and wants to interactively browse the remote host, so set the + * login_verbosity to whatever it is set to now. + */ + *login_verbosity = verbose; + + if (openopt->colonmodepath[0] != 0) { + /* But if the user does use colon-mode, we want to do our business + * and leave, without all the login messages, etc., so set + * login_verbosity to quiet so we won't print anything until + * we finish. Colon-mode can be specified from the shell command + * line, so we would like to be able to execute ncftp as a one + * line command from the shell without spewing gobs of output. + */ + *login_verbosity = V_QUIET; + } else if (openopt->ftpcat != 0) { + /* User specified ftpcat mode, but didn't supply the host:file. */ + (void) fprintf(stderr, "You didn't use colon mode correctly.\n\ +If you use -c or -m, you need to do something like this:\n\ + ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n"); + return USAGE; + } + return NOERR; +} /* CheckForColonMode */ + + + + +/* All this short routine does is to hookup a socket to either the + * remote host or the firewall gateway host. + */ +int HookupToRemote(OpenOptions *openopt) +{ + int hErr; + +#ifdef GATEWAY + /* Try connecting to the gateway host. */ + if (*gateway) { + hErr = hookup(gateway, openopt->port); + (void) Strncpy(hostname, openopt->hostname); + } else +#endif + hErr = hookup(openopt->hostname, openopt->port); + + return hErr; +} /* HookupToRemote */ + + + + +void CheckRemoteSystemType(int force_binary) +{ + int tmpverbose; + char *cp, c; + + /* As of this writing, UNIX is pretty much standard. */ + remote_is_unix = 1; + + /* Do a SYSTem command quietly. */ + tmpverbose = verbose; + verbose = V_QUIET; + if (command("SYST") == COMPLETE) { + if (tmpverbose == V_VERBOSE) { + /* Find the system type embedded in the reply_string, + * and separate it from the rest of the junk. + */ + cp = index(reply_string+4, ' '); + if (cp == NULL) + cp = index(reply_string+4, '\r'); + if (cp) { + if (cp[-1] == '.') + cp--; + c = *cp; + *cp = '\0'; + } + + (void) printf("Remote system type is %s.\n", + reply_string+4); + if (cp) + *cp = c; + } + remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4); + } + + /* Set to binary mode if any of the following are true: + * (a) The user has auto-binary set; + * (b) The user is using colon-mode (force_binary); + * (c) The reply-string from SYST said it was UNIX with 8-bit chars. + */ + if (auto_binary || force_binary + || !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) { + (void) _settype("binary"); + if (tmpverbose > V_TERSE) + (void) printf("Using binary mode to transfer files.\n"); + } + + /* Print a warning for that (extremely) rare Tenex machine. */ + if (tmpverbose >= V_ERRS && + !strncmp(reply_string, "215 TOPS20", (size_t) 10)) { + (void) _settype("tenex"); + (void) printf("Using tenex mode to transfer files.\n"); + } + verbose = tmpverbose; +} /* CheckRemoteSystemType */ + + + +/* This is called if the user opened the host with a file appended to + * the host's name, like "wuarchive.wustl.edu:/pub/readme," or + * "wuarchive.wustl.edu:/pub." In the former case, we open wuarchive, + * and fetch "readme." In the latter case, we open wuarchive, then set + * the current remote directory to "/pub." If we are fetching a file, + * we can do some other tricks if "ftpcat mode" is enabled. This mode + * must be selected from your shell's command line, and this allows you + * to use the program as a one-liner to pipe a remote file into something, + * like "ncftp -c wu:/pub/README | wc." If the user uses ftpcat mode, + * the program immediately quits instead of going into it's own command + * shell. + */ +void ColonMode(OpenOptions *openopt) +{ + int tmpverbose; + + /* How do we tell if colonmodepath is a file or a directory? + * We first try cd'ing to the path first. If we can, then it + * was a directory. If we could not, we'll assume it was a file. + */ + + /* Shut up, so cd won't print 'foobar: Not a directory.' */ + tmpverbose = verbose; + verbose = V_QUIET; + + /* If we are using ftpcat|more mode, or we couldn't cd to the + * colon-mode path (then it must be a file to fetch), then + * we need to fetch a file. + */ + if (openopt->ftpcat || ! _cd(openopt->colonmodepath)) { + /* We call the appropriate fetching routine, so we have to + * have the argc and argv set up correctly. To do this, + * we just make an entire command line, then let makeargv() + * convert it to argv/argc. + */ + if (openopt->ftpcat == FTPCAT) + (void) sprintf(line, "get %s -", openopt->colonmodepath); + else if (openopt->ftpcat == FTPMORE) + (void) sprintf(line, "more %s", openopt->colonmodepath); + else { + /* Regular colon-mode, where we fetch the file, putting the + * copy in the current local directory. + */ + (void) sprintf(line, "mget %s", openopt->colonmodepath); + } + makeargv(); + + /* Turn on messaging if we aren't catting. */ + if (openopt->ftpcat == 0) + verbose = tmpverbose; + + /* get() also handles 'more'. */ + if (openopt->ftpcat) + (void) get(margc, margv); + else + (void) mget(margc, margv); + + /* If we were invoked from the command line, quit + * after we got this file. + */ + if (eventnumber == 0L) { + (void) quit(0, NULL); + } + } + verbose = tmpverbose; +} /* ColonMode */ + + + + +/* Given a properly set up OpenOptions, we try connecting to the site, + * redialing if necessary, and do some initialization steps so the user + * can send commands. + */ +int Open(OpenOptions *openopt) +{ + int hErr; + int dials; + char *ruser, *rpass, *racct; + int siteInRC; + char *user, *pass, *acct; + int login_verbosity, oldv; + + macnum = 0; /* Reset macros. */ + + /* If the hostname supplied is in the form host.name.str:/path/file, + * then colon mode was used, and we need to fix the hostname to be + * just the hostname, copy the /path/file to colonmode path, and init + * the login_verbosity variable. + */ + if (CheckForColonMode(openopt, &login_verbosity) == USAGE) + return USAGE; + + /* If the hostname supplied was an abbreviation, such as just + * "wu" (wuarchive.wustl.edu), look through the list of sites + * we know about and get the whole name. We also would like + * the path we want to start out in, if it is available. + */ + GetFullSiteName(openopt->hostname, openopt->cdpath); + +#ifdef GATEWAY + /* Make sure the gateway host name is a full name and not an + * abbreviation. + */ + if (*gateway) + GetFullSiteName(gateway, NULL); +#endif + + ruser = rpass = racct = NULL; + /* This also loads the init macro. */ + siteInRC = ruserpass2(openopt->hostname, &ruser, &rpass, &racct); + if (ISANONOPEN(openopt->openmode)) { + user = "anonymous"; + pass = anon_password; + } else { + user = NULL; + pass = NULL; + } + acct = NULL; + + if (siteInRC && !openopt->ignore_rc) { + acct = racct; + if (ruser != NULL) { + /* We were given a username. If we were given explicit + * instructions from the command line, follow those and + * ignore what the RC had. Otherwise if no -a or -u + * was specified, we use whatever was in the RC. + */ + if (ISIMPLICITOPEN(openopt->openmode)) { + user = ruser; + pass = rpass; + } + } + } + + for ( + dials = 0; + openopt->max_dials < 0 || dials < openopt->max_dials; + dials++) + { + if (dials > 0) { + /* If this is the second dial or higher, sleep a bit. */ + (void) sleep(openopt->redial_delay); + (void) fprintf(stderr, "Retry Number: %d\n", dials + 1); + } + + if ((hErr = HookupToRemote(openopt)) == -2) + /* Recoverable, so we can try re-dialing. */ + continue; + else if (hErr == NOERR) { + /* We were hookup'd successfully. */ + connected = 1; + + oldv = verbose; verbose = login_verbosity; + +#ifdef GATEWAY + if (*gateway) { + if ((Login( + user, + pass, + acct, + (!openopt->ignore_rc && !openopt->colonmodepath[0]) + ) != NOERR) || cout == NULL) + goto nextdial; /* error! */ + } +#endif + +#ifdef GATEWAY + if (!*gateway) { +#endif + /* We don't want to run the init macro for colon-mode. */ + if ((Login( + user, + pass, + acct, + (!openopt->ignore_rc && !openopt->colonmodepath[0]) + ) != NOERR) || cout == NULL) + { + goto nextdial; /* error! */ + } +#ifdef GATEWAY + } +#endif + + verbose = oldv; + + /* We need to check for unix and see if we should set binary + * mode automatically. + */ + CheckRemoteSystemType(openopt->colonmodepath[0] != (char)0); + + if (openopt->colonmodepath[0]) { + ColonMode(openopt); + } else if (openopt->cdpath[0]) { + /* If we didn't have a colon-mode path, we try setting + * the current remote directory to cdpath. cdpath is + * usually the last directory we were in the previous + * time we called this site. + */ + (void) _cd(openopt->cdpath); + } else { + /* Freshen 'cwd' variable for the prompt. + * We have to do atleast one 'cd' so our variable + * cwd (which is saved by _cd()) is set to something + * valid. + */ + (void) _cd(NULL); + } + break; /* we are connected, so break the redial loop. */ + /* end if we are connected */ + } else { + /* Irrecoverable error, so don't bother redialing. */ + /* The error message should have already been printed + * from Hookup(). + */ + break; + } +nextdial: + disconnect(0, NULL); + continue; /* Try re-dialing. */ + } + return (NOERR); +} /* Open */ + + + +/* This stub is called by our command parser. */ +int cmdOpen(int argc, char **argv) +{ + OpenOptions openopt; + + /* If there is already a site open, close that one so we can + * open a new one. + */ + if (connected && NOT_VQUIET && hostname[0]) { + (void) printf("Closing %s...\n", hostname); + (void) disconnect(0, NULL); + } + + /* Reset the remote info structure for the new site we want to open. + * Assume we have these properties until we discover otherwise. + */ + gRmtInfo.hasSIZE = 1; + gRmtInfo.hasMDTM = 1; + + if ((GetOpenOptions(argc, argv, &openopt) == USAGE) || + (Open(&openopt) == USAGE)) + return USAGE; + return NOERR; +} /* cmdOpen */ + +/* eof open.c */ diff --git a/usr.bin/ncftp/open.h b/usr.bin/ncftp/open.h new file mode 100644 index 000000000000..7be8ccbbe1d5 --- /dev/null +++ b/usr.bin/ncftp/open.h @@ -0,0 +1,50 @@ +/* open.h */ + +#ifndef _open_h_ +#define _open_h_ 1 + +/* Variables for Open() that can be changed from the command line. */ +typedef struct OpenOptions { + int openmode; + int ignore_rc; + unsigned int port; + int redial_delay; + int max_dials; + int ftpcat; + Hostname hostname; + longstring cdpath; + longstring colonmodepath; +} OpenOptions; + +typedef struct RemoteSiteInfo { + int hasSIZE; + int hasMDTM; +} RemoteSiteInfo; + +/* Open modes. */ +#define openImplicitAnon 1 +#define openImplicitUser 4 +#define openExplicitAnon 3 +#define openExplicitUser 2 + +#define ISUSEROPEN(a) ((a==openImplicitUser)||(a==openExplicitUser)) +#define ISANONOPEN(a) (!ISUSEROPEN(a)) +#define ISEXPLICITOPEN(a) ((a==openExplicitAnon)||(a==openExplicitUser)) +#define ISIMPLICITOPEN(a) (!ISEXPLICITOPEN(a)) + +/* ftpcat modes. */ +#define NO_FTPCAT 0 +#define FTPCAT 1 +#define FTPMORE 2 + +/* Protos: */ +void InitOpenOptions(OpenOptions *openopt); +int GetOpenOptions(int argc, char **argv, OpenOptions *openopt); +int CheckForColonMode(OpenOptions *openopt, int *login_verbosity); +int HookupToRemote(OpenOptions *openopt); +void CheckRemoteSystemType(int); +void ColonMode(OpenOptions *openopt); +int Open(OpenOptions *openopt); +int cmdOpen(int argc, char **argv); + +#endif /* _open_h_ */ diff --git a/usr.bin/ncftp/patchlevel.h b/usr.bin/ncftp/patchlevel.h new file mode 100644 index 000000000000..99690ee07cf3 --- /dev/null +++ b/usr.bin/ncftp/patchlevel.h @@ -0,0 +1,127 @@ +v1.8.5 - September 20, 1994. Better support for term. + +v1.8.4 - September 19, 1994. Bug in Makefile fixed. Bug involving getwd + fixed. + +v1.8.3 - August 27, 1994. Bug fixed where failed connection attempts when + using a numeric IP address would fail. + +v1.8.2 - August 4, 1994. Can compile with DONT_TIMESTAMP to turn off syncing + timestamps of fetched files with their remote counterparts. IP_TOS now + utilized. + +v1.8.1 - July 4, 1994. Forgot <signal.h> in ftprc.c. + +v1.8.0 - July 4, 1994. Tweak for DEC OSF/1. NO_FORMATTING added. + Support for QNX added. Reporting an error if the recent file could + not be written. Bumped up the max recents to 50; the open menu will + now be fed through your pager to avoid the problem of scrolling off + screen. Fixed problem with redialing and running out of descriptors. + +v1.7.8 - June 30, 1994. No longer defining TERMH for linux. + +v1.7.7 - June 21, 1994. Deleted a space in front of an " #endif". + No functionality change whatsoever... + +v1.7.6 - June 18, 1994. Added commands and code to support the + PASV command for passive negotiation of the data connection from + the host server to the client. This facilitates operation of the + client software from within a firewall. (J. B. Harrell) + +v1.7.5 - May 28, 1994. Fixed a rare problem with dimmed text. Fixed + compilation problem with Dynix. Defining the domain name now takes + precedence over the getdomainname() function. + +v1.7.4 - May 16, 1994. Tweaked hookup() a bit, to (try to) handle + hosts with multiple addresses better. Fixed error with GMT offsets. + Fixed 'addr_t' typo in SVR4 section. Moved SVR4 block down in sys.h. + +v1.7.3 - April 13, 1994. Fixed minor error in syslog reporting. + Trying both getpwnam() and getpwuid(), instead of just one of them, + increasing the probability the program can get your passwd entry. + Better compatibility with other types of password input devices. + +v1.7.2 - April 5, 1994. Bytes/sec is now correct. Fixed error when + NO_VARARGS was defined. Added support for <varargs.h>. + +v1.7.1 - March 27, 1994. Defining HAS_DOMAINNAME for NeXT. Term hack can + share sockets, plus some term stuff added to the Makefile. Trimmed + some old stuff from the patchlevel.h file, and putting new versions + first now. Smarter about determining abbreviations from local hostnames. + Fixed bug where progress meter would go beserk after trying to get + a non-existant file. + +v1.7.0 - March 14, 1994. More verbose when logging to the system log, + and making sure that syslog() itself is called with a total of 5 + or less parameters. Official patch posted which incorporates all + the fixes to 1.6.0 (i.e. 1.6.1, 1.6.2, ... 1.6.9). + +v1.6.9 - March 11, 1994. Added DOMAIN_NAME and Solaris CPP symbols. + Better handling of getting the domain name, specifically with SunOS. + BSDi support added. + +v1.6.8 - March 4, 1994. Ensuring that tmp files are not public. + Trying harder to get the real hostname, and fixed problem with + disappearing progress meters (both due to T. Lindgren). + +v1.6.7 - February 20, 1994. Using getpwnam() instead of getpwuid(). + Supporting WWW paths (i.e. ftp://host.name/path/name). + +v1.6.6 - February 15, 1994. Prevented scenario of fclosing a NULL FILE *. + Edited term ftp's hookup() a little. More defs for linux in sys.h. + Not updating a recent entry unless you were fully logged in. + +v1.6.5 - January 6, 1994. Fixed error with an #ifndef/#endif block having + whitespace before the #. No longer confirming "ls >file" actions. + Changed echo() to Echo(). AIX 3 uses TERMIOS. + +v1.6.4 - December 30, 1993. Fixed rare problem with GetDateAndTime. + confirm() will return true if you're running the init macro. + +v1.6.3 - December 28, 1993. Added a new diagnostic command, memchk, + to print stats from a special malloc library if you used one. + Using SIZE and MDTM when the remote site supports it. Using a new + set of routines for term (again). + +v1.6.2 - December 10, 1993. + Term hack no longer depends on the PASV command (!). The BROKEN_MEMCPY + problem worked-around. More wary of symbolic-link recursion. + Fixed local path expander. Fixed inadvertant flushing of the typeahead + buffer. Debug mode won't print your password. Progress meters + no longer goof up when the file is huge. Added time-remaining to the + Philbar. + +v1.6.1 - November 5, 1993. + Checking if we have permission to write over a file to fetch. + A few very minor changes. BSD no longer trying to use strchr :-) + +v1.6.0 - October 31, 1993. + Added "term" support for Linux users. Better SCO Xenix support. Added + -DLINGER, if you have a connection requiring it (so 'puts' to the remote + system complete). Added -DNET_ERRNO_H if you need to include + <net/errno.h>. Including more headers in sys.h. Fixed another globulize + bug. Fixed a bug in confirm() where prompt was overwriting a str32. + Added -DNO_CURSES_H if you do not want to try and include <curses.h>. + Added -DHAS_GETCWD (usually automatic) and HAS_DOMAINNAME. Logins as + "ftp" act like "anonymous." Fixed bug with "open #x". Making sure you + defined GZCAT as a string. Turning off termcap attributes one by one, + instead of using the turn-off-all-attributes. A few fixes for the man + page, including documentation of the progress-meter types. AIX 2.x, + AIX 3.x, ISC Unix, Dynix/PTX, and Besta support added to sys.h. Safer + use of getwd(). Colon-mode is quieter. Getuserinfo function tweaked. + Eliminated unnecessary GetHomeDir function in util.c. Reworked Gets(), + since it wasn't always stripping \n's. Recent file can now read dir + names with whitespace. Opening msg uses a larger buffer, because of + escape codes. Philbar now prints K/sec stats. + +v1.5.6 - September 20, 1993... +v1.5.5 - September 16, 1993... +v1.5.4 - September 14, 1993... +v1.5.3 - September 2, 1993... +v1.5.2 - August 30, 1993... +v1.5.1 - August 29, 1993... +v1.5.0 - August 22, 1993... + +v1.0.2 - Jan 17, 1993... +v1.0.1 - December 8, 1992... +v1.0.0 - December 6, 1992. Initial release. diff --git a/usr.bin/ncftp/set.c b/usr.bin/ncftp/set.c new file mode 100644 index 000000000000..14d87150520f --- /dev/null +++ b/usr.bin/ncftp/set.c @@ -0,0 +1,367 @@ +/* Set.c */ + +/* $RCSfile: set.c,v $ + * $Revision: 14020.12 $ + * $Date: 93/07/09 11:45:48 $ + */ + +#include "sys.h" + +#include <ctype.h> + +#include "util.h" +#include "cmds.h" +#include "main.h" +#include "set.h" +#include "defaults.h" +#include "copyright.h" + +#ifdef TERM_FTP +extern int compress_toggle; +#endif + +/* Set.c globals: */ +char *verbose_msgs[4] = { + "Not printing anything.\n", + "Only printing necessary error messages.\n", + "Printing error messages and announcements from the remote host.\n", + "Printing all messages, errors, acknowledgments, and announcements.\n" +}; + +char *short_verbose_msgs[4] = { + "Quiet (-1)", + "Errors Only (0)", + "Terse (1)", + "Verbose (2)" +}; + +string vstr; + +/* Set.c externs: */ +extern int progress_meter, connected; +extern int parsing_rc, keep_recent; +extern string pager, anon_password, prompt; +extern str32 curtypename; +extern long logsize; +extern FILE *logf; +extern longstring rcname, logfname, lcwd; +extern int auto_binary, ansi_escapes, debug; +extern int mprompt, remote_is_unix, verbose; +extern int startup_msg, anon_open; +#ifndef NO_TIPS +extern int tips; +#endif +#ifdef GATEWAY +extern string gateway, gate_login; +#endif + +/* The variables must be sorted in alphabetical order, or else + * match_var() will choke. + */ +struct var vars[] = { + VARENTRY("anon-open", BOOL, 0, &anon_open, NULL), + VARENTRY("anon-password", STR, 0, anon_password, NULL), + VARENTRY("ansi-escapes", BOOL, 0, &ansi_escapes, NULL), + VARENTRY("auto-binary", BOOL, 0, &auto_binary, NULL), +#ifdef TERM_FTP + VARENTRY("compress", INT, 0, + &compress_toggle,NULL), +#endif + VARENTRY("debug", INT, 0, &debug, NULL), +#ifdef GATEWAY + VARENTRY("gateway-login", STR, 0, gate_login, set_gatelogin), + VARENTRY("gateway-host", STR, 0, gateway, NULL), +#endif + VARENTRY("local-dir", STR, 0, lcwd, set_ldir), + VARENTRY("logfile", STR, 0, logfname, set_log), + VARENTRY("logsize", LONG, 0, &logsize, NULL), + VARENTRY("mprompt", BOOL, 0, &mprompt, NULL), + VARENTRY("netrc", -STR, 0, rcname, NULL), + VARENTRY("pager", STR, 0, pager + 1, set_pager), + VARENTRY("prompt", STR, 0, prompt, set_prompt), + VARENTRY("progress-reports",INT, 0, &progress_meter,NULL), + VARENTRY("recent-list", BOOL, 0, &keep_recent, NULL), + VARENTRY("remote-is-unix", BOOL, 1, &remote_is_unix,NULL), + VARENTRY("startup-msg", BOOL, 0, &startup_msg, NULL), /* TAR */ +#ifndef NO_TIPS + VARENTRY("tips", BOOL, 0, &tips, NULL), +#endif + VARENTRY("type", STR, 1, curtypename, set_type), + VARENTRY("verbose", STR, 0, vstr, set_verbose), +}; + + +void set_verbose(char *new, int unset) +{ + int i, c; + + if (unset == -1) verbose = !verbose; + else if (unset || !new) verbose = V_ERRS; + else { + if (isalpha(*new)) { + c = islower(*new) ? toupper(*new) : *new; + for (i=0; i<(int)(sizeof(short_verbose_msgs)/sizeof(char *)); i++) { + if (short_verbose_msgs[i][0] == c) + verbose = i - 1; + } + } else { + i = atoi(new); + if (i < V_QUIET) i = V_QUIET; + else if (i > V_VERBOSE) i = V_VERBOSE; + verbose = i; + } + } + (void) Strncpy(vstr, short_verbose_msgs[verbose+1]); + if (!parsing_rc && NOT_VQUIET) + (void) fputs(verbose_msgs[verbose+1], stdout); +} /* set_verbose */ + + + + +void set_prompt(char *new, int unset) +{ + (void) Strncpy(prompt, (unset || !new) ? dPROMPT : new); + init_prompt(); +} /* set_prompt */ + + + + +void set_log(char *fname, int unset) +{ + if (logf) { + (void) fclose(logf); + logf = NULL; + } + if (!unset && fname) { + (void) Strncpy(logfname, fname); + logf = fopen (LocalDotPath(logfname), "a"); + } +} /* set_log */ + + + + +void set_pager(char *new, int unset) +{ + if (unset) + (void) strcpy(pager, "-"); + else { + if (!new) + new = dPAGER; + if (!new[0]) + (void) Strncpy(pager, "-"); + else { + (void) sprintf(pager, "|%s", (*new == '|' ? new + 1 : new)); + (void) LocalPath(pager + 1); + } + } +} /* set_pager */ + + + + +void set_type(char *newtype, int unset) +{ + int t = verbose; + verbose = V_QUIET; + if (!connected && t > V_QUIET) + (void) printf("Not connected.\n"); + else if (newtype != NULL && !unset) + (void) _settype(newtype); + verbose = t; +} /* set_type */ + + + + +void set_ldir(char *ldir, int unset) +{ + int t = verbose; + char *argv[2]; + + if (ldir && !unset) { + verbose = V_QUIET; + argv[1] = ldir; + (void) lcd(2, argv); + verbose = t; + } +} /* set_ldir */ + + + + +#ifdef GATEWAY +void set_gatelogin(char *glogin, int unset) +{ + if (unset || !glogin) { + gate_login[0] = gateway[0] = 0; + } else + (void) strcpy(gate_login, glogin); +} /* set_gatelogin */ +#endif + + + + +struct var *match_var(char *varname) +{ + int i, ambig; + struct var *v; + short c; + + c = (short) strlen(varname); + for (i=0, v=vars; i<NVARS; i++, v++) { + if (strcmp(v->name, varname) == 0) + return v; /* exact match. */ + if (c < v->nmlen) { + if (strncmp(v->name, varname, (size_t) c) == 0) { + /* Now make sure that it only matches one var name. */ + if (c >= v[1].nmlen || (i == (NVARS - 1))) + ambig = 0; + else + ambig = !strncmp(v[1].name, varname, (size_t) c); + if (!ambig) + return v; + (void) fprintf(stderr, "%s: ambiguous variable name.\n", varname); + goto xx; + } + } + } + (void) fprintf(stderr, "%s: unknown variable.\n", varname); +xx: + return ((struct var *)0); +} /* match_var */ + + + + +void show_var(struct var *v) +{ + int c; + + if (v != (struct var *)0) { + (void) printf("%-20s= ", v->name); + c = v->type; + if (c < 0) c = -c; + if (v->conn_required && !connected) + (void) printf("(not connected)\n"); + else switch (c) { + case INT: + (void) printf("%d\n", *(int *)v->var); break; + case LONG: + (void) printf("%ld\n", *(long *)v->var); break; + case STR: + (void) printf("\"%s\"\n", (char *)v->var); break; + case BOOL: + (void) printf("%s\n", *(int *)v->var == 0 ? "no" : "yes"); + } + } +} /* show_var */ + + + + +void show(char *varname) +{ + int i; + struct var *v; + + if ((varname == NULL) /* (Denotes show all vars) */ + || (strcmp("all", varname) == 0)) + { + for (i=0; i<NVARS; i++) + show_var(&vars[i]); + } else { + if ((v = match_var(varname)) != (struct var *)0) + show_var(v); + } +} /* show */ + + + + +int do_show(int argc, char **argv) +{ + int i; + + if (argc < 2) + show(NULL); + else + for (i=1; i<argc; i++) + show(argv[i]); + return NOERR; +} /* do_show */ + + + + +int set(int argc, char **argv) +{ + int unset; + struct var *v; + char *var, *val = NULL; + + if (argc < 2 || strncmp(argv[1], "all", (size_t)3) == 0) { + show(NULL); /* show all variables. */ + } else { + unset = argv[0][0] == 'u'; + var = argv[1]; + if (argc > 2) { + /* could be '= value' or just 'value.' */ + if (*argv[2] == '=') { + if (argc > 3) + val = argv[3]; + else return USAGE; /* can't do 'set var =' */ + } else + val = argv[2]; + if (val[0] == 0) + val = NULL; + } + v = match_var(var); + if (v != NULL) { + if (v->conn_required && !connected) + (void) fprintf(stderr, "%s: must be connected.\n", var); + else if (v->type < 0) + (void) fprintf(stderr, "%s: read-only variable.\n", var); + else if (v->proc != (setvarproc) 0) { + (*v->proc)(val, unset); /* a custom set proc. */ + } else if (unset) switch(v->type) { + case BOOL: + case INT: + *(int *) v->var = 0; break; + case LONG: + *(long *) v->var = 0; break; + case STR: + *(char *) v->var = 0; break; + } else { + if (val == NULL) switch(v->type) { + /* User just said "set varname" */ + case BOOL: + case INT: + *(int *) v->var = 1; break; + case LONG: + *(long *) v->var = 1; break; + case STR: + *(char *) v->var = 0; break; + } else { + /* User said "set varname = value" */ + switch (v->type) { + case BOOL: + *(int *)v->var = StrToBool(val); break; + case INT: + (void) sscanf(val, "%d", (int *) v->var); break; + case LONG: + (void) sscanf(val, "%ld", (long *) v->var); break; + case STR: + (void) strcpy(v->var, val); break; + } + } + } + } + } + return NOERR; +} /* set */ + +/* eof Set.c */ diff --git a/usr.bin/ncftp/set.h b/usr.bin/ncftp/set.h new file mode 100644 index 000000000000..92f7125f1864 --- /dev/null +++ b/usr.bin/ncftp/set.h @@ -0,0 +1,46 @@ +/* Set.h */ + +#ifndef _set_h_ +#define _set_h_ + +/* $RCSfile: set.h,v $ + * $Revision: 14020.11 $ + * $Date: 93/06/26 06:21:32 $ + */ + +/* Variable types. */ +#define INT 1 +#define LONG 2 +#define STR 3 +#define BOOL 4 + +typedef void (*setvarproc)(char *, int); +struct var { + char *name; + short nmlen; + short type; + short conn_required; + void *var; + setvarproc proc; +}; + +#define VARENTRY(n,t,c,v,p) { (n), (short)(sizeof(n) - 1), (t), (c), (v), (setvarproc)(p) } +#define NVARS ((int) (sizeof(vars)/sizeof(struct var))) + +void set_prompt(char *new, int unset); +void set_log(char *fname, int unset); +void set_ldir(char *ldir, int unset); +#ifdef GATEWAY +void set_gateway(char *, int); +void set_gatelogin(char *, int); +#endif +void set_pager(char *new, int unset); +void set_verbose(char *new, int unset); +void set_type(char *newtype, int unset); +struct var *match_var(char *varname); +void show_var(struct var *v); +void show(char *varname); +int do_show(int argc, char **argv); +int set(int argc, char **argv); + +#endif /* _set_h_ */ diff --git a/usr.bin/ncftp/sys.h b/usr.bin/ncftp/sys.h new file mode 100644 index 000000000000..699432fcd617 --- /dev/null +++ b/usr.bin/ncftp/sys.h @@ -0,0 +1,611 @@ +/* Sys.h + * See the README for details. + */ + +/* $RCSfile: sys.h,v $ + * $Revision: 14020.13 $ + * $Date: 93/06/21 06:42:11 $ + */ + + +#ifdef __sun +# ifndef sun +# define sun 1 +# endif +#endif + +#ifdef sun +# if !defined(__GNUC__) && !defined(__STDC__) && !defined(SunOverride) + /* If you choke here, but you know what you're doing, just + * define SunOverride. + */ + ^^^ "You need to use an ANSI C compiler. Try using gcc or acc." ^^^ +# endif +# ifdef Solaris /* not predefined. */ +# define SYSV 1 +# define System "Solaris" +# undef __STDC__ +# define __STDC__ 0 +# else +# define System "SunOS" +# ifndef RINDEX +# define RINDEX 1 +# endif +# endif /* not Solaris */ +# ifndef TERMIOS +# define TERMIOS 1 +# endif +# ifndef HAS_DOMAINNAME +# define HAS_DOMAINNAME 1 +# endif +#endif /* sun */ + +#ifdef __sgi +# ifndef sgi +# define sgi 1 +# endif +#endif + +#ifdef sgi +# define System "IRIX" +# ifndef SYSV +# define SYSV 1 +# endif +# ifndef HERROR +# define HERROR 1 +# endif +# ifndef U_WAIT +# define U_WAIT 1 +# endif +# ifndef STRICT_PROTOS +# define STRICT_PROTOS 1 +# endif +# ifndef TERMIOS +# define TERMIOS 1 +# endif +#endif /* sgi */ + +#ifdef AIX +# define System "AIX 2.2.1" +# define BSD_INCLUDES +# define SYSV +# define NO_STDLIB +# define NO_UTIME_H +# define NO_STRFTIME +# define NO_STRSTR +# define NO_MKTIME +#endif /* AIX */ + +#ifdef _AIX +# define System "AIX 3.x" +# define SYSSELECTH 1 +# define TERMIOS 1 +#endif /* _AIX */ + +#ifdef __QNX__ +# define QNX +# define System "QNX 4.21 (POSIX)" +# define SYSSELECTH +# define TERMIOS +# define _POSIX_SOURCE +# define GETCWDSIZET +# define STRICT_PROTOS +# define RINDEX +# define NO_CURSES_H +# define unlink remove +# define bcopy(s,d,l) memcpy((d),(s),(l)) +# define bzero(cp,l) memset((cp),0,(l)) +# define NO_SYSPARAM +# include <limits.h> +# define NCARGS _POSIX_ARG_MAX +#endif + +#ifdef SCOXNX +# define System "SCO Xenix" +# define LAI_TCP +# define NO_UTIMEH +# define NO_MKTIME +# define NO_STRFTIME +# define NO_STRSTR +# define NO_RENAME +# define LINGER /* else SCO bug causes incomplete transfers */ +# define SYSV 1 +#endif /* SCOXNX */ + +#ifdef SCO322 +# define System "SCO Unix 3.2v2" +# define BOTCHED_FOPEN_RW +# define NO_RENAME /* it exists, but it corrupts filesystems */ +# define BROKEN_MEMCPY 1 +# define SYSV 1 +#endif /* SCO322 */ + +#ifdef SCO324 +# define System "SCO Unix 3.2v4" +# ifndef SYSV +# define SYSV 1 +# endif +# ifndef BROKEN_MEMCPY +# define BROKEN_MEMCPY 1 +# endif +#endif /* SCO324 */ + +#ifdef linux +# define System "Linux" +# ifndef HAS_DOMAINNAME +# define HAS_DOMAINNAME 1 +# endif +# ifndef TERMIOS +# define TERMIOS 1 +# endif +# ifndef SYSV +# define SYSV 1 +# endif +#endif + +#ifdef ISC +# define System "Interactive Unix" +# ifndef SYSV +# define SYSV 1 +# endif +# ifndef BROKEN_MEMCPY +# define BROKEN_MEMCPY 1 +# endif +# ifndef NET_ERRNO_H +# define NET_ERRNO_H 1 +# endif +#endif /* ISC */ + +#ifdef aux +# define System "A/UX" +# ifndef BROKEN_MEMCPY +# define BROKEN_MEMCPY 1 +# endif +# ifndef SYSV +# define SYSV 1 +# endif +#endif + +#ifdef NeXT +# define System "NeXTStep" +# ifndef RINDEX +# define RINDEX 1 +# endif +# ifndef BSD +# define BSD 1 +# endif +# ifndef NO_UNISTDH +# define NO_UNISTDH 1 +# endif +# ifndef NO_UTIMEH +# define NO_UTIMEH +# endif +# ifndef HAS_DOMAINNAME +# define HAS_DOMAINNAME 1 +# endif +#endif + +#ifdef pyr +# define System "OSx" +# ifndef BSD +# define BSD 1 +# endif +# ifndef SGTTYB +# define SGTTYB 1 +# endif +# ifndef NO_STDLIBH +# define NO_STDLIBH 1 +# endif +extern int errno; +#endif /* pyr */ + +#ifdef _SEQUENT_ +# if !defined(DYNIXPTX) && !defined(DYNIX) +# define DYNIXPTX 1 +# endif +#endif + +#if DYNIXPTX +# define System "Dynix/PTX" +# ifndef SYSV +# define SYSV 1 +# endif +# ifndef TRY_NOREPLY +# define TRY_NOREPLY 1 +# endif +# define gettimeofday(a, b) get_process_stats(a, getpid(), 0, 0) +#endif /* DYNIXPTX */ + +#ifdef DYNIX +# define System "Dynix" +# ifndef BSD +# define BSD 1 +# endif +# ifndef SGTTYB +# define SGTTYB 1 +# endif +# ifndef NO_UTIMEH +# define NO_UTIMEH 1 +# endif +# ifndef NO_STDLIBH +# define NO_STDLIBH 1 +# endif +# ifndef NO_VARARGS +# define NO_VARARGS 1 +# endif +#endif /* DYNIX */ + +#ifdef ultrix +# define System "Ultrix" +# ifndef BSD +# define BSD 1 +# endif +# ifndef USE_GETPWUID +# define USE_GETPWUID 1 +# endif +# ifndef __GNUC__ +# ifndef NO_CONST +# define NO_CONST 1 +# endif +# endif +#endif /* ultrix */ + +#ifdef __hpux +# ifndef HPUX +# define HPUX 1 +# endif +# define Select(a,b,c,d,e) select((a), (int *)(b), (c), (d), (e)) +#endif + +#ifdef HPUX +# define System "HP-UX" +# ifndef _HPUX_SOURCE +# define _HPUX_SOURCE 1 +# endif +# ifndef GETCWDSIZET +# define GETCWDSIZET 1 +# endif +# define SYSV 1 +#endif /* HPUX */ + +#ifdef SINIX +# define System "SINIX" +# ifndef SYSV +# define SYSV 1 +# endif +/* You may need to add -lresolv, -lport, -lcurses to MORELIBS in Makefile. */ +#endif + +#ifdef BULL /* added 23nov92 for Bull DPX/2 */ +# define _POSIX_SOURCE +# define _XOPEN_SOURCE +# define _BULL_SOURCE +# ifndef SYSV +# define SYSV 1 +# endif +# define bull +# define System "Bull DPX/2 BOS" +# define SYSSELECTH +#endif /* BULL */ /* added 23nov92 for Bull DPX/2 */ + +#ifdef __dgux +# ifndef DGUX +# define DGUX 1 +# endif +#endif + +#ifdef DGUX +# ifndef _DGUX_SOURCE +# define _DGUX_SOURCE +# endif +# define GETCWDSIZET 1 +# define BAD_INETADDR 1 +# define SYSV 1 +# define System "DG/UX" +#endif /* DGUX */ + +#ifdef apollo +# ifndef BSD +# define BSD 43 +# endif +# define SIG_PARAMS (int sig, ...) +# define NO_UTIMEH 1 +# define System "Apollo" +#endif + +#ifdef __Besta__ +# define SYSV 1 +# define SYSSELECTH 1 +# define NO_UNISTDH 1 +# define NO_STDLIBH 1 +# define NO_UTIMEH 1 +# ifndef BROKEN_MEMCPY +# define BROKEN_MEMCPY 1 +# endif +# include <sys/types.h> +#endif + +#ifdef __osf__ +# ifdef __alpha /* DEC OSF/1 */ +# define GETCWDSIZET 1 +# endif +#endif + +/* -------------------------------------------------------------------- */ + +#ifdef _SYSV +# ifndef SYSV +# define SYSV 1 +# endif +#endif + +#ifdef USG +# ifndef SYSV +# define SYSV 1 +# endif +#endif + +#ifdef _BSD +# ifndef BSD +# define BSD 1 +# endif +#endif + +#ifdef SVR4 +# ifndef System +# define System "System V.4" +# endif +# ifndef SYSV +# define SYSV 1 +# endif +# ifndef VOID +# define VOID void +# endif +# ifndef HERROR +# define HERROR 1 +# endif +# ifdef TERMH +# define TERMH 1 +# endif +# ifndef Gettimeofday +# define Gettimeofday gettimeofday +# endif +#endif /* SVR4 */ + +#ifdef SYSV +# ifndef RINDEX +# define RINDEX 1 +# endif +# define bcopy(s,d,l) memcpy((d),(s),(l)) +# define bzero(cp,l) memset((cp),0,(l)) +# ifndef HAS_GETCWD +# define HAS_GETCWD 1 +# endif +#endif + +#ifdef __bsdi__ +# define System "BSDi" +# ifndef BSD +# define BSD 1 +# endif +# ifndef SYSSELECTH +# define SYSSELECTH 1 +# endif +# ifndef GETCWDSIZET +# define GETCWDSIZET 1 +# endif +# ifndef HERROR +# define HERROR 1 +# endif +#endif /* BSDi */ + +#ifdef __386BSD__ +# ifdef __FreeBSD__ +# define System "FreeBSD" +# define GZCAT "/usr/bin/gzcat" +# define HAS_DOMAINNAME 1 +# endif +# include <sys/types.h> +# include <sys/param.h> /* this two for BSD definition */ + /* to avoid redefinition of it to 1 */ +# define HERROR 1 +# define TERMIOS 1 +# define HAS_GETCWD 1 +# define U_WAIT 1 +# define NO_CONST 1 /* avoid prototype conflict */ +#endif + +#ifdef BSD +# ifndef __386BSD__ +# ifndef SYSDIRH +# define SYSDIRH 1 +# endif +# ifndef SGTTYB +# define SGTTYB +# endif +# endif +#endif + +/* + * Generic pointer type, e.g. as returned by malloc(). + */ +#ifndef PTRTYPE +# define PTRTYPE void +#endif + +#ifndef Free +# define Free(a) free((PTRTYPE *)(a)) +#endif + +/* + * Some systems besides System V don't use rindex/index (like SunOS). + * Add -DRINDEX to your SDEFS line if you need to. + */ +#ifdef RINDEX + /* or #include <strings.h> if you have it. */ +# define rindex strrchr +# define index strchr +#endif /* RINDEX */ + +#ifdef SOCKS +#define Getsockname(d,a,l) Rgetsockname((d), (struct sockaddr *)(a), (l)) +#else +#ifdef SYSV +# define Getsockname(d,a,l) getsockname((d), (void *)(a), (l)) +#else +# define Getsockname(d,a,l) getsockname((d), (struct sockaddr *)(a), (l)) +#endif +#endif + +#ifndef Select +# define Select(a,b,c,d,e) select((a), (b), (c), (d), (e)) +#endif + +#ifndef Connect +#ifndef SVR4 +# define Connect(a,b,c) (connect((a), (struct sockaddr *)(b), (int)(c))) +# define Bind(a,b,c) (bind((a), (struct sockaddr *)(b), (int)(c))) +# define Accept(a,b,c) (accept((a), (struct sockaddr *)(b), (int *)(c))) +#else /* SVR4 */ +# define Connect(a,b,c) (connect((a), (caddr_t)(b), (int)(c))) +# define Bind(a,b,c) (bind((a), (caddr_t)(b), (int)(c))) +# define Accept(a,b,c) (accept((a), (caddr_t)(b), (int *)(c))) +#endif /* SVR4 */ +#endif /* Connect */ + +#ifndef Gettimeofday +# define Gettimeofday(a) gettimeofday(a, (struct timezone *)0) +#endif /* Gettimeofday */ + +#ifdef GETPASS +# define Getpass getpass +#endif + +/* Enable connections through firewall gateways */ +#ifndef GATEWAY +# define GATEWAY 1 +#endif + +#ifdef _POSIX_SOURCE +# define TERMIOS +#endif + +/* Include frequently used headers: */ + +#include <sys/types.h> + +#ifndef NO_SYSPARAM +#include <sys/param.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <sys/socket.h> + +#include <sys/time.h> +#include <time.h> + +#ifndef NO_STDLIBH +# include <stdlib.h> +#else +extern PTRTYPE *malloc(size_t); +extern PTRTYPE *calloc(size_t, size_t); +extern PTRTYPE *malloc(size_t); +extern void free(PTRTYPE *); +extern PTRTYPE *realloc(PTRTYPE *, size_t); +extern void exit(int); + +#ifdef NO_CONST +extern char *getenv(char *); +extern int atoi(char *); +#else +extern char *getenv(const char *); +extern int atoi(const char *); +#endif + +#endif /* NO_STDLIBH */ + +#ifndef NO_UNISTDH +# include <unistd.h> +#else +char *getlogin (void); +# ifdef NO_CONST +extern char *getenv(char *); +# else +extern char *getenv(const char *); +# endif +#endif /* NO_UNISTDH */ + +#ifdef NO_STD_PROTOS +extern int _filbuf(FILE *); +extern int _flsbuf(int, FILE *); +extern int fflush(FILE *); +extern int fgetc(FILE *); +extern int fprintf(FILE *, char *, ...); +extern int fputc(int, FILE *); +extern int fputs(char *, FILE *); +extern int fclose(FILE *); +extern int pclose(FILE *); +extern void perror(char *); +extern int printf(char *, ...); +extern int rewind(FILE *); +extern int sscanf(char *, char *, ...); +extern int vfprintf(FILE *, char *, char *); + +extern char * mktemp(char *); +extern int rename(char *, char *); + +extern int gettimeofday(struct timeval *, struct timezone *); +extern time_t mktime(struct tm *); +extern int strftime(char *, int, char *, struct tm *); +extern time_t time(time_t *); + +extern int tolower(int); +extern int toupper(int); + +#ifndef bcopy +extern void bcopy(char *, char *, size_t); +#endif +#ifndef bzero +extern void bzero(char *, size_t); +#endif + +#ifdef SOCKS +extern int Raccept(int, struct sockaddr *, int *); +extern int Rbind(int, struct sockaddr *, int, unsigned long); +extern int Rconnect(int, struct sockaddr *, int); +extern int Rlisten(int, int); +extern int Rgetsockname(int, struct sockaddr *, int *); +#else +extern int accept(int, struct sockaddr *, int *); +extern int bind(int, struct sockaddr *, int); +extern int connect(int, struct sockaddr *, int); +extern int listen(int, int); +extern int getsockname(int, struct sockaddr *, int *); +#endif +extern int gethostname(char *, int), getdomainname(char *, int); +#ifndef Select +extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +#endif +extern int send(int, char *, int, int); +extern int setsockopt(int, int, int, char *, int); +extern int shutdown(int, int); +extern int socket(int, int, int); +#endif /* NO_STD_PROTOS */ + +/* This malloc stuff is mostly for our own use. */ +#define LIBC_MALLOC 0 +#define FAST_MALLOC 1 +#define DEBUG_MALLOC 2 + +#ifdef LIBMALLOC +# if LIBMALLOC != LIBC_MALLOC + /* Make sure you use -I to use the malloc.h of choice. */ +# include <malloc.h> +# endif +#else +# define LIBMALLOC LIBC_MALLOC +#endif +/* End of personal malloc junk. */ + +/* eof sys.h */ diff --git a/usr.bin/ncftp/tips.c b/usr.bin/ncftp/tips.c new file mode 100644 index 000000000000..5f7267164d99 --- /dev/null +++ b/usr.bin/ncftp/tips.c @@ -0,0 +1,147 @@ +/* tips.c */ + +/* $RCSfile: tips.c,v $ + * $Revision: 14020.11 $ + * $Date: 93/05/21 05:44:39 $ + */ + +#include "sys.h" + +#ifndef NO_TIPS + +#include "util.h" + +/* Make sure that the indentations are spaces, not tabs. + * Try newform -i-4 < tips.c > tips.c.new + * + * Always add new tips right above the last one. + */ + +static char *tiplist[] = { + "Have you tried typing 'open' by itself lately?", + + "If you don't want a .ncrecent file in your home directory, put the \n\ + command '#unset recent-list' in your .ncftprc file.", + + "pseudo-filename-completion is supported in some commands. To use it,\n\ + use a wildcard expression that will match exactly one file. I.e., if you\n\ + want to fetch obnoxiouslylongfilename.zip, try 'get obn*.zip.' Note that\n\ + you can't use the cd command with this feature (yet).", + + "You don't need to type the exact site name with open. If a site is in\n\ + your .ncftprc or the recent-file (.ncrecent), just type a unique\n\ + abbreviation (substring really). I.e. 'open wuar' if you have the site\n\ + wuarchive.wustl.edu in your rc or recent-file.", + + "You can put set commands in your .ncftprc, by adding lines such\n\ + as '#set local-dir /usr/tmp' to the file, which will be run at startup.", + + "Use the .ncftprc file to set variables at startup and to add sites that \n\ + need init macros.\n\ + Sample .ncftprc:\n\ + #set pager \"less -M\"\n\ + \n\ + machine wuarchive.wustl.edu\n\ + macdef init\n\ + cd /pub\n\ + get README\n\ + dir\n\ + (blank line to end macro)", + + "If you want to keep your .netrc's for ftp and ncftp separate, name\n\ + ncftp's rc to .ncftprc.", + + "Type 'open' by itself to get a list of the sites in your recent-file and\n\ + your .ncftprc. You can then supply '#5' at the prompt, or use 'open #5'\n\ + later.", + + "Colon-mode is a quick way to get a file from your shell. Try something\n\ + like 'ncftp wuarchive.wustl.edu:/pub/README.'", + + "The open command accepts several flags. Do a 'help open' for details.", + + "Sometimes a directory listing is several screens long and you won't\n\ + remember the thing you wanted. Use the 'predir' command to re-view the\n\ + listing. The program keeps the copy locally, so you won't have to wait\n\ + for the remote server to re-send it to you.", + + "Use the 'page' (or 'more') command to view a remote file with your pager.", + + "ncftp may be keeping detailed information on everything you transfer.\n\ + Run the 'version' command and if you see SYSLOG, your actions are being\n\ + recorded on the system log.", + + "Try the 'redir' command to re-display the last directory listing (ls,\n\ + dir, ls -lrt, etc). 'predir' does the same, only with your pager.", + + "This program is pronounced Nik-F-T-P. NCEMRSoft is Nik'-mer-soft.", + +#ifdef GETLINE + "NcFTP was compiled with the Getline command-line/history editor! (by\n\ + Chris Thewalt <thewalt@ce.berkeley.edu>). To activate it, use the up\n\ + and down arrows to scroll through the history, and/or use EMACS-style\n\ + commands to edit the line.", +#endif + +#ifdef READLINE + "NcFTP was compiled with the GNU Readline command-line/history editor!\n\ + To activate it, use the up & down arrows to scroll through the history,\n\ + and/or use EMACS-style (or maybe VI-style) commands to edit the line.", +#endif + + "You can get the newest version of NcFTP from cse.unl.edu, in the\n\ + /pub/mgleason/ncftp directory, AFTER business hours.", + + "The type of progress-meter that will be used depends if the remote host\n\ + supports the SIZE command, and whether your terminal is capable of ANSI\n\ + escape codes.", + + "To report a bug, mail your message to mgleason@cse.unl.edu. Include the\n\ + output of the 'version' command in your message. An easy way to do that\n\ + is to compose your message, then do a 'ncftp -H >> msg.'", + + "Don't put a site in your .ncftprc unless you want an 'init' macro. The \n\ + recent-file saves sites with the last directory you were in, unlike \n\ + the rc file, while still letting you use sitename abbreviations.", + + "You can use World Wide Web style paths instead of colon-mode paths.\n\ + For example, if the colon-mode path was 'cse.unl.edu:pub/mgleason/ncftp',\n\ + the WWW-style path would be 'ftp://cse.unl.edu/pub/mgleason/ncftp'.", + + "Sick and tired of these tips? Put '#unset tips' in your .ncftprc." +}; + +/* Not another dinky header, por favor. */ +#define NTIPS ((int) (sizeof(tiplist) / sizeof(char *))) +void PrintTip(void); +extern int fromatty, debug; + +int tips = 1; +#endif /* NO_TIPS */ + +void PrintTip(void) +{ +#ifndef NO_TIPS + int cheap_rn, i, tn; + string str; + + if (tips && fromatty) { + cheap_rn = (int) getpid() % NTIPS; + if (debug) { + (void) printf("pid: %d; ntips: %d\n", getpid(), NTIPS); + (void) Gets("*** Tip# (-1 == all): ", str, sizeof(str)); + tn = atoi(str) - 1; + if (tn == -1) + tn = 0; + if (tn < -1) + for(i=0; i<NTIPS; i++) + (void) printf("Tip: %s\n", tiplist[i]); + else if (tn < NTIPS) + (void) printf("Tip: %s\n", tiplist[tn]); + } else + (void) printf("Tip: %s\n", tiplist[cheap_rn]); + } +#endif /* NO_TIPS */ +} /* PrintTip */ + +/* tips.c */ diff --git a/usr.bin/ncftp/util.c b/usr.bin/ncftp/util.c new file mode 100644 index 000000000000..ee2fba35d8dc --- /dev/null +++ b/usr.bin/ncftp/util.c @@ -0,0 +1,922 @@ +/* Util.c */ + +/* $RCSfile: util.c,v $ + * $Revision: 14020.13 $ + * $Date: 93/05/23 09:38:13 $ + */ + +#include "sys.h" + +#include <errno.h> +#include <ctype.h> +#include <pwd.h> + +#ifndef NO_VARARGS +# ifdef NO_STDARGH +# include <varargs.h> +# else +# include <stdarg.h> +# endif +#endif + +#ifdef READLINE +# include <readline/readline.h> +#endif /* READLINE */ + +#ifdef GETLINE +# include <getline.h> +#endif + +#include "util.h" +#include "cmds.h" +#include "main.h" +#include "ftp.h" +#include "ftprc.h" +#include "defaults.h" +#include "copyright.h" + +/* Util.c globals */ +int Opterr = 1; /* if error message should be printed */ +int Optind = 1; /* index into parent argv vector */ +int Optopt; /* character checked for validity */ +char *Optarg; /* argument associated with option */ +char *Optplace = EMSG; /* saved position in an arg */ + +/* Util.c externs */ +extern int toatty, fromatty; +extern int verbose, doingInitMacro; +extern string prompt2; +extern char *line, *margv[]; +extern int margc; +extern int debug, mprompt, activemcmd; +extern string progname; +extern struct cmd cmdtab[]; +extern struct userinfo uinfo; + +#ifndef NO_VARARGS +/*VARARGS*/ +#ifdef NO_STDARGH +void dbprintf(va_alist) + va_dcl +#else +void dbprintf(char *fmt0, ...) +#endif +{ + va_list ap; + char *fmt; + +#ifdef NO_STDARGH + va_start(ap); + fmt = va_arg(ap, char *); +#else + va_start(ap, fmt0); + fmt = fmt0; +#endif + + if (debug) { + (void) fprintf(DB_STREAM, "#DB# "); + (void) vfprintf(DB_STREAM, fmt, ap); + (void) fflush(DB_STREAM); + } + va_end(ap); +} /* dbprintf */ + +#endif /* have varargs */ + + + + +/* + * Concatenate src on the end of dst. The resulting string will have at most + * n-1 characters, not counting the NUL terminator which is always appended + * unlike strncat. The other big difference is that strncpy uses n as the + * max number of characters _appended_, while this routine uses n to limit + * the overall length of dst. + */ +char *_Strncat(char *dst, char *src, register size_t n) +{ + register size_t i; + register char *d, *s; + + if (n != 0 && ((i = strlen(dst)) < (n - 1))) { + d = dst + i; + s = src; + /* If they specified a maximum of n characters, use n - 1 chars to + * hold the copy, and the last character in the array as a NUL. + * This is the difference between the regular strncpy routine. + * strncpy doesn't guarantee that your new string will have a + * NUL terminator, but this routine does. + */ + for (++i; i<n; i++) { + if ((*d++ = *s++) == 0) { + /* Pad with zeros. */ + for (; i<n; i++) + *d++ = 0; + return dst; + } + } + /* If we get here, then we have a full string, with n - 1 characters, + * so now we NUL terminate it and go home. + */ + *d = 0; + } + return (dst); +} /* _Strncat */ + + +/* + * Copy src to dst, truncating or null-padding to always copy n-1 bytes. + * Return dst. + */ +char *_Strncpy(char *dst, char *src, register size_t n) +{ + register char *d; + register char *s; + register size_t i; + + d = dst; + *d = 0; + if (n != 0) { + s = src; + /* If they specified a maximum of n characters, use n - 1 chars to + * hold the copy, and the last character in the array as a NUL. + * This is the difference between the regular strncpy routine. + * strncpy doesn't guarantee that your new string will have a + * NUL terminator, but this routine does. + */ + for (i=1; i<n; i++) { + if ((*d++ = *s++) == 0) { + /* Pad with zeros. */ + for (; i<n; i++) + *d++ = 0; + return dst; + } + } + /* If we get here, then we have a full string, with n - 1 characters, + * so now we NUL terminate it and go home. + */ + *d = 0; + } + return (dst); +} /* _Strncpy */ + + + +/* Converts any uppercase characters in the string to lowercase. + * Never would have guessed that, huh? + */ +void StrLCase(char *dst) +{ + register char *cp; + + for (cp=dst; *cp != '\0'; cp++) + if (isupper((int) *cp)) + *cp = (char) tolower(*cp); +} + + + + +char *Strpcpy(char *dst, char *src) +{ + while ((*dst++ = *src++) != '\0') + ; + return (--dst); /* return current value of dst, NOT original value! */ +} /* Strpcpy */ + + + +/* + * malloc's a copy of oldstr. + */ +char *NewString(char *oldstr) +{ + size_t howLong; + char *newstr; + + howLong = strlen(oldstr); + if ((newstr = malloc(howLong + 1)) != NULL) + (void) strcpy(newstr, oldstr); + return newstr; +} /* NewString */ + + + + + +void Getopt_Reset(void) +{ + Optind = 1; + Optplace = ""; +} /* Getopt_Reset */ + +static char *NextOption(char *ostr) +{ + if ((Optopt = (int) *Optplace++) == (int) ':') + return 0; + return index(ostr, Optopt); +} + +int Getopt(int nargc, char **nargv, char *ostr) +{ + register char *oli; /* Option letter list index */ + + if (!*Optplace) { /* update scanning pointer */ + if (Optind >= nargc || *(Optplace = nargv[Optind]) != '-') + return (EOF); + if (Optplace[1] && *++Optplace == '-') { /* found "--" */ + ++Optind; + return (EOF); + } + } /* Option letter okay? */ + oli = NextOption(ostr); + if (oli == NULL) { + if (!*Optplace) + ++Optind; + if (Opterr) { + (void) fprintf(stderr, "%s%s%c\n", *nargv, ": illegal option -- ", Optopt); + return(BADCH); + } + } + if (*++oli != ':') { /* don't need argument */ + Optarg = NULL; + if (!*Optplace) + ++Optind; + } else { /* need an argument */ + if (*Optplace) /* no white space */ + Optarg = Optplace; + else if (nargc <= ++Optind) { /* no arg */ + Optplace = EMSG; + if (Opterr) { + (void) fprintf(stderr, "%s%s%c\n", *nargv, ": option requires an argument -- ", Optopt); + return(BADCH); + } + } else /* white space */ + Optarg = nargv[Optind]; + Optplace = EMSG; + ++Optind; + } + return (Optopt); /* dump back Option letter */ +} /* Getopt */ + + + + + +/* + * Converts an ls date, in either the "Feb 4 1992" or "Jan 16 13:42" + * format to a time_t. + */ +unsigned long UnLSDate(char *dstr) +{ +#ifdef NO_MKTIME + return (MDTM_UNKNOWN); +#else + char *cp = dstr; + int mon, day, year, hr, min; + time_t now, mt; + unsigned long result = MDTM_UNKNOWN; + struct tm ut, *t; + + switch (*cp++) { + case 'A': + mon = (*cp == 'u') ? 7 : 3; + break; + case 'D': + mon = 11; + break; + case 'F': + mon = 1; + break; + default: /* shut up un-init warning */ + case 'J': + if (*cp++ == 'u') + mon = (*cp == 'l') ? 6 : 5; + else + mon = 0; + break; + case 'M': + mon = (*++cp == 'r') ? 2 : 4; + break; + case 'N': + mon = 10; + break; + case 'O': + mon = 9; + break; + case 'S': + mon = 8; + } + cp = dstr + 4; + day = 0; + if (*cp != ' ') + day = 10 * (*cp - '0'); + cp++; + day += *cp++ - '0'; + min = 0; + + (void) time(&now); + t = localtime(&now); + + if (*++cp != ' ') { + /* It's a time, XX:YY, not a year. */ + cp[2] = ' '; + (void) sscanf(cp, "%d %d", &hr, &min); + cp[2] = ':'; + year = t->tm_year; + if (mon > t->tm_mon) + --year; + } else { + hr = min = 0; + (void) sscanf(cp, "%d", &year); + year -= 1900; + } + /* Copy the whole structure of the 'tm' pointed to by t, so it will + * also set all fields we don't specify explicitly to be the same as + * they were in t. That way we copy non-standard fields such as + * tm_gmtoff, if it exists or not. + */ + ut = *t; + ut.tm_sec = 1; + ut.tm_min = min; + ut.tm_hour = hr; + ut.tm_mday = day; + ut.tm_mon = mon; + ut.tm_year = year; + ut.tm_wday = ut.tm_yday = 0; + mt = mktime(&ut); + if (mt != (time_t) -1) + result = (unsigned long) mt; + return (result); +#endif /* NO_MKTIME */ +} /* UnLSDate */ + + + +/* + * Converts a MDTM date, like "213 19930602204445\n" + * format to a time_t. + */ +unsigned long UnMDTMDate(char *dstr) +{ +#ifdef NO_MKTIME + return (MDTM_UNKNOWN); +#else + struct tm ut; + time_t mt; + unsigned long result = MDTM_UNKNOWN; + + /* Clear out the whole structure, along with any non-standard fields. */ + bzero((char *)&ut, sizeof (struct tm)); + + if (sscanf(dstr, "%*s %04d%02d%02d%02d%02d%02d", + &ut.tm_year, + &ut.tm_mon, + &ut.tm_mday, + &ut.tm_hour, + &ut.tm_min, + &ut.tm_sec) == 6) + { + --ut.tm_mon; + ut.tm_year -= 1900; + mt = mktime(&ut); + if (mt != (time_t) -1) + result = (unsigned long) mt; + } + return result; +#endif /* NO_MKTIME */ +} /* UnMDTMDate */ + + + +void Perror( +#ifdef DB_ERRS + char *fromProc + , +#ifdef __LINE__ + int lineNum, +#endif +#endif + char *msg + ) +{ + extern int errno; + + if (NOT_VQUIET) { +#ifdef sun + /* + * There is a problem in the SunOS headers when compiling with an ANSI + * compiler. The problem is that there are macros in the form of + * #define MAC(x) 'x', and this will always be the character x instead + * of whatever parameter was passed to MAC. If we get these errors, it + * usually means that you are trying to compile with gcc when you haven't + * run the 'fixincludes' script that fixes these macros. We will ignore + * the error, but it means that the echo() function won't work correctly, + * and you will see your password echo. + */ + if (errno == ENOTTY) + return; +#endif + (void) fprintf(stderr, "NcFTP"); +#ifdef DB_ERRS + if (fromProc != NULL) + (void) fprintf(stderr, "/%s", fromProc); +#ifdef __LINE__ + (void) fprintf(stderr, "/%d", lineNum); +#endif +#endif + (void) fprintf(stderr, ": "); + if (msg != NULL) + (void) fprintf(stderr, "%s (%d): ", msg, errno); + perror(NULL); + } +} /* Perror */ + + + + +size_t RemoveTrailingNewline(char *cp, int *stripped) +{ + size_t len; + int nBytesStripped = 0; + + if (cp != NULL) { + cp += (len = strlen(cp)) - 1; + if (*cp == '\n') { + *cp-- = 0; /* get rid of the newline. */ + nBytesStripped++; + } + if (*cp == '\r') { /* no returns either, please. */ + *cp = 0; + nBytesStripped++; + } + if (stripped != NULL) + *stripped = nBytesStripped; + return len; + } + return (size_t)0; +} /* RemoveTrailingNewline */ + + + +#ifdef GETLINE +extern size_t epromptlen; + +/* + * The Getline library doesn't detect the ANSI escape sequences, so the + * library would think that a string is longer than actually appears on + * screen. This function lets Getline work properly. This function is + * intended to fix that problem for the main command prompt only. If any + * other prompts want to use ANSI escapes, a (costly) function would have + * to scan the prompt for all escape sequences. + */ +/*ARGSUSED*/ +static size_t MainPromptLen(char *pr) +{ + return (int)epromptlen; +} +#endif + +static char *StdioGets(char *promptstr, char *sline, size_t size) +{ + char *cp; + + if (fromatty) { + /* It's okay to print a prompt if we are redirecting stdout, + * as long as stdin is still a tty. Otherwise, don't print + * a prompt at all if stdin is redirected. + */ +#ifdef CURSES + tcap_put(promptstr); +#else + (void) fputs(promptstr, stdout); +#endif + } + sline[0] = 0; + (void) fflush(stdout); /* for svr4 */ + cp = fgets(sline, (int)(size - 2), stdin); + (void) RemoveTrailingNewline(sline, NULL); + return cp; +} /* StdioGets */ + + +/* Given a prompt string, a destination string, and it's size, return feedback + * from the user in the destination string, with any trailing newlines + * stripped. Returns NULL if EOF encountered. + */ +char *Gets(char *promptstr, char *sline, size_t size) +{ + char *cp, ch; + string plines; +#ifdef GETLINE + int ismainprompt = (promptstr == prompt2); +#endif + + if (!fromatty || !toatty) { + /* Don't worry about a cmdline/history editor if you redirected a + * file at me. + */ + return (StdioGets(promptstr, sline, size)); + } + + sline[0] = 0; /* Clear it, in case of an error later. */ + + /* + * The prompt string may actually be several lines if the user put a + * newline in it with the @N option. In this case we only want to print + * the very last line, so the command-line editors won't screw up. So + * now we print all the lines except the last line. + */ + cp = rindex(promptstr, '\n'); + if (cp != NULL) { + ch = *++cp; + *cp = 0; + (void) Strncpy(plines, promptstr); + *cp = ch; + promptstr = cp; +#ifdef CURSES + tcap_put(plines); +#else + (void) fputs(plines, stdout); +#endif + } + +#ifdef READLINE + if ((cp = readline(promptstr)) != NULL) { + (void) _Strncpy(sline, cp, size); + free(cp); + (void) RemoveTrailingNewline(cp = sline, NULL); + if (*cp != 0) /* Don't add blank lines to history buffer. */ + add_history(cp); + } +#else /* READLINE */ + +#ifdef GETLINE + if (toatty) { + if (ismainprompt) + gl_strwidth(MainPromptLen); + if ((cp = getline(promptstr)) != NULL) { + if (*cp == '\0') /* You hit ^D. */ + return NULL; + cp = _Strncpy(sline, cp, size); + (void) RemoveTrailingNewline(cp, NULL); + if (*cp != '\0') { /* Don't add blank lines to history buffer. */ + gl_histadd(cp); + } + } + /* Hope your strlen is declared as returning a size_t. */ + gl_strwidth(strlen); + } else { + cp = StdioGets(promptstr, sline, size); + } +#else /* !GETLINE */ + cp = StdioGets(promptstr, sline, size); +#endif /* !GETLINE */ +#endif /* !READLINE */ + return cp; +} /* Gets */ + + + + +char **re_makeargv(char *promptstr, int *argc) +{ + size_t sz; + + (void) strcat(line, " "); + sz = strlen(line); + (void) Gets(promptstr, &line[sz], (size_t) (CMDLINELEN - sz)) ; + (void) makeargv(); + *argc = margc; + return (margv); +} /* re_makeargv */ + + + +#ifndef HAS_GETCWD +extern char *getwd(char *); +#endif + +char *get_cwd(char *buf, int size) +{ +#ifdef HAS_GETCWD +# ifdef NO_UNISTDH +# ifdef GETCWDSIZET + extern char *getcwd(char *, size_t); +# else + extern char *getcwd(char *, int); +# endif +# endif + return (getcwd(buf, size - 1)); +#else +#ifndef MAXPATHLEN +# define MAXPATHLEN (1024) +#endif + static char *cwdbuf = NULL; + + if (cwdbuf == NULL) { + cwdbuf = (char *)malloc((size_t) MAXPATHLEN); + if (cwdbuf == NULL) + fatal("out of memory for getwd buffer."); + } + getwd(cwdbuf); + return (_Strncpy(buf, cwdbuf, (size_t)size)); +#endif +} /* get_cwd */ + + + +int tmp_name(char *str) +{ + (void) strcpy(str, "/tmp/ncftpXXXXXX"); + return (!mktemp(str)); +} /* tmp_name */ + + + + +char *onoff(int boolf) +{ + return (boolf ? "on" : "off"); +} /* onoff */ + + + + +int StrToBool(char *s) +{ + int c; + int result; + + c = tolower(*s); + result = 0; + switch (c) { + case 'f': /* false */ + case 'n': /* no */ + break; + case 'o': /* test for "off" and "on" */ + c = tolower(s[1]); + if (c == 'f') + break; + /* fall through */ + case 't': /* true */ + case 'y': /* yes */ + result = 1; + break; + default: /* 1, 0, -1, other number? */ + if (atoi(s) != 0) + result = 1; + } + return result; +} /* StrToBool */ + + + + +int confirm(char *cmd, char *file) +{ + string str, pr; + + if (!fromatty || (activemcmd && !mprompt) || (doingInitMacro)) + return 1; + (void) sprintf(pr, "%s %s? ", cmd, file); + (void) Gets(pr, str, sizeof(str)); + return (*str != 'n' && *str != 'N'); +} /* confirm */ + + + +void fatal(char *msg) +{ + (void) fprintf(stderr, "%s: %s\n", progname, msg); + close_up_shop(); + exit(1); +} /* fatal */ + + + + +int UserLoggedIn(void) +{ + static int inited = 0; + static int parent_pid, stderr_was_tty; + + if (!inited) { + stderr_was_tty = isatty(2); + parent_pid = getppid(); + inited++; + } + if ((stderr_was_tty && !isatty(2)) || (getppid() != parent_pid)) + return 0; + return 1; +} /* UserLoggedIn */ + + + + +struct cmd *getcmd(char *name) +{ + struct cmd *c, *found; + int nmatches; + size_t len; + char *p; + + found = (struct cmd *)0; + if (name != NULL) { + len = strlen(name); + nmatches = 0; + for (c = cmdtab; (p = c->c_name) != NULL; c++) { + if (strcmp(name, p) == 0) { + /* Exact match. */ + found = c; + goto xx; + } + if (c->c_handler == unimpl) + continue; + if (strncmp(name, p, len) == 0) { + if (++nmatches > 1) { + found = ((struct cmd *) -1); + goto xx; + } + found = c; + } else if (found != NULL) + break; + } + } +xx: + return (found); +} /* getcmd */ + + + + +void cmd_help(struct cmd *c) +{ + (void) printf("%s: %s.\n", + c->c_name, + c->c_help + ); +} /* cmd_help */ + + + + +void cmd_usage(struct cmd *c) +{ + if (c->c_usage != NULL) + (void) printf("Usage: %s%s\n", + c->c_name, + c->c_usage + ); +} /* cmd_usage */ + + + + +/* + * A simple function that translates most pathnames with ~, ~user, or + * environment variables as the first item. It won't do paths with env vars + * or ~s in the middle of the path, but those are extremely rare. + */ +char *LocalPath(char *path) +{ + longstring orig; + struct passwd *pw; + char *firstent; + char *cp, *dp, *rest; + + (void) Strncpy(orig, path); + firstent = orig; + if ((cp = index(orig, '/')) != NULL) { + if (cp == orig) { + /* If we got here, the path is actually a full path name, + * with the first character as a slash, so just leave it + * alone. + */ + return (path); + } + /* Otherwise we can look at the first word of the path, and + * try to expand it, like $HOME/ or ~/, or it is a relative path, + * which is okay since we won't really do anything with it. + */ + *cp = 0; + rest = cp + 1; + /* 'firstent' now contains the first 'word' in the path. */ + } else { + /* Path was just a single word, or it is a full path, like: + * /usr/tmp/zz, so firstent is just the entire given "path." + */ + rest = NULL; + } + if (orig[0] == '~') { + if (orig[1] == 0) { + firstent = uinfo.homedir; + } else { + pw = getpwnam(orig + 1); + if (pw != NULL) + firstent = pw->pw_dir; + } + } else if (orig[0] == '$') { + cp = orig + 1; + dp = orig + strlen(orig) - 1; + if ((*cp == '(' && *dp == ')') || (*cp == '{' && *dp == '}')) { + cp++; + *dp = 0; + } + firstent = getenv(cp); + if (firstent == NULL) { + (void) fprintf(stderr, "%s: no such environment variable.\n", cp); + firstent = "badEnvVar"; + } + } + if (rest == NULL) + (void) strcpy(path, firstent); + else + (void) sprintf(path, "%s/%s", firstent, rest); + return (path); +} /* LocalPath */ + + + +/* + * A special case, where invisible dot-files that would normally appear in + * your home directory will appear instead as visible files in your $DOTDIR + * directory if you have one. + */ + +#define LCMP(b) (strncmp(path, (b), (o = sizeof(b) - 1)) == 0) + +char *LocalDotPath(char *path) +{ + size_t o; + longstring s, s2; + char *cp = getenv("DOTDIR"); + + if (cp == NULL) { + goto aa; + } else { + if (*cp != '/' && *cp != '~') { + /* then maybe they mean relative to $HOME. */ + (void) sprintf(s2, "%s/%s", uinfo.homedir, cp); + cp = s2; + } + if (LCMP("~/.") || + LCMP("$HOME/.") || + LCMP("$home/.") || + LCMP("$(HOME)/.") || + LCMP("${HOME}/.") + ) { + (void) Strncpy(s, path); + (void) sprintf(path, "%s/%s", cp, s + o); + cp = path; + } else { +aa: cp = LocalPath(path); + } + } + return cp; +} /* LocalDotPath */ + +#ifdef NO_STRSTR + +/* + * The Elm Mail System - $Revision: 5.1 $ $State: Exp $ + * + * Copyright (c) 1988-1992 USENET Community Trust + * Copyright (c) 1986,1987 Dave Taylor + */ + +char *strstr(s1, s2) +char *s1, *s2; +{ + int len; + char *ptr; + char *tmpptr; + + ptr = NULL; + len = strlen(s2); + + if ( len <= strlen(s1)) { + tmpptr = s1; + while ((ptr = index(tmpptr, (int)*s2)) != NULL) { + if (strncmp(ptr, s2, len) == 0) { + break; + } + tmpptr = ptr+1; + } + } + return (ptr); +} + +#endif + + +#ifdef NO_RENAME +int rename(oldname, newname) +const char *oldname, *newname; +{ + return (link(oldname, newname) == 0 ? unlink(oldname) : -1); +} +#endif /*NO_RENAME*/ + + +/* eof Util.c */ diff --git a/usr.bin/ncftp/util.h b/usr.bin/ncftp/util.h new file mode 100644 index 000000000000..64babbfcaa1c --- /dev/null +++ b/usr.bin/ncftp/util.h @@ -0,0 +1,104 @@ +/* Util.h */ + +#ifndef _util_h_ +#define _util_h_ + +/* $RCSfile: util.h,v $ + * $Revision: 14020.12 $ + * $Date: 93/07/09 11:32:49 $ + */ + +typedef char string[128], str32[32], longstring[512]; +typedef char Hostname[64]; + +/* For Perror. */ +#ifdef DB_ERRS +# ifdef __LINE__ +# define PERROR(p,e) Perror(p, __LINE__, e) + void Perror(char *, int, char *); +# else +# define PERROR(p,e) Perror(p, e) + void Perror(char *, char *); +# endif +#else +# define PERROR(p,e) Perror(e) + void Perror(char *); +#endif + +#ifdef NO_VARARGS + extern int debug; +# define dbprintf if (debug) (void) printf +#else +# ifndef DB_STREAM +# define DB_STREAM stdout +# endif +#ifndef NO_STDARGH +void dbprintf(char *fmt0, ...); +#else +void dbprintf(int va_alist); +#endif +#endif + +/* For 'Getopt.' */ +#define BADCH ((int)'?') +#define EMSG "" + +/* Handy macros. */ +#define Strncpy(d,s) _Strncpy((char *) (d), (char *) (s), (size_t) sizeof(d)) +#define Strncat(d,s) _Strncat((char *) (d), (char *) (s), (size_t) sizeof(d)) +#define FGets(a,b) fgets((a), (int) (sizeof(a) - 2), (b)) + +#ifndef NO_CONST +typedef int (*cmp_t)(const void *, const void *); +#else +typedef int (*cmp_t)(void *, void *); +#endif + +#define QSort(base,n,sz,cmp) \ + qsort(base, (size_t)(n), (size_t)(sz), (cmp_t)(cmp)) + +#ifndef SIG_PARAMS +#define SIG_PARAMS (int sig) +#endif +typedef void (*Sig_t) SIG_PARAMS; + +#define Signal(a,proc) signal((a), (Sig_t)(proc)) + +/* Quiets warnings */ +#if defined(sun) /* ...actually, any UNIX system */ +# if defined(__GNUC__) +# undef SIG_DFL +# undef SIG_IGN +# define SIG_DFL (Sig_t)0 +# define SIG_IGN (Sig_t)1 +# endif +#endif + +/* Protos. */ +char *_Strncat(char *dst, char *src, register size_t n); +char *_Strncpy(char *dst, char *src, register size_t n); +void StrLCase(char *dst); +char *NewString(char *oldstr); +char **re_makeargv(char *promptstr, int *argc); +char *onoff(int); +int StrToBool(char *s); +int confirm(char *cmd, char *file); +void fatal(char *msg); +char *get_cwd(char *buf, int size); +int tmp_name(char *str); +int Getopt(int argc, char **argv, char *opstring); +void Getopt_Reset(void); +char *Gets(char *promptstr, char *sline, size_t size); +size_t RemoveTrailingNewline(char *cp, int *stripped); +unsigned long UnLSDate(char *dstr); +unsigned long UnMDTMDate(char *dstr); +char *Strpcpy(char *dst, char *src); +int UserLoggedIn(void); +char *LocalPath(char *path); +char *LocalDotPath(char *path); + +#ifdef NO_STRSTR +char *strstr(char *s1, char *s2); +#endif + +#endif /* _util_h_ */ diff --git a/usr.bin/ncftp/v2_Note b/usr.bin/ncftp/v2_Note new file mode 100644 index 000000000000..6900bf29d9c2 --- /dev/null +++ b/usr.bin/ncftp/v2_Note @@ -0,0 +1,35 @@ +Versions numbered between 1.5.0 and 1.9.9 are interim releases. No major +features are planned for these; only tweaks and fixes. + +Version 2.0 is much cooler, but I haven't had time to work on it. If +you send me email, I _will_ read it, but it may take me awhile to get to it, +and I may not answer. Please don't be offended. I really regret having +to release code that is such a mess. I should have wrote the code from +scratch, rather than built it upon the original BSD code (which is a mess +itself!). + +I apologize in advance for bugs I fixed in 2.0 but forgot to re-fix +in these interim releases (I think I got'em all, though). + +Here is a list of things that are in the 2.0 code but not this version: + +* Easy-to-read, better organized, commented code. +* Using my own "style guide," so source is coded uniformly. +* Unlimited global macros. These are really nice! They take arguments, + so you can make mini-scripts. The macros can be typed as if they + were commands; no more $macroname crap. +* No-longer using .netrc and it's format. This was necessary for the + global macros anyway, and it allowed me to add some other features, + like host aliases. Old .netrc's won't work. Sorry! +* Improved command line parser, that lets any command use > and |, so + it behaves like a real shell command line (almost). +* Improved 'redir' that works automatically, can reformat listings with + different ls flags, all without refetching it over the network. It + will also facilitate remote globbing... AND remote filename completion! +* Many little things I can't begin to list. + +Keep that in mind if you want to make a feature enhancement. I'm telling +you this now so you don't spend your time programming something that is +already done for the 2.0 release. Also note that patches would +have to be re-coded for 2.0. (The gist is that you should wait until +2.0 is done before doing anything major). |