aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey A. Chernov <ache@FreeBSD.org>1994-09-22 23:45:37 +0000
committerAndrey A. Chernov <ache@FreeBSD.org>1994-09-22 23:45:37 +0000
commitb7b69dce5b689dd1cddf5dbc778a125cc5afe296 (patch)
tree31cb31bfb69915fc791ce9c2ec7d345124bedebf
downloadsrc-b7b69dce5b689dd1cddf5dbc778a125cc5afe296.tar.gz
src-b7b69dce5b689dd1cddf5dbc778a125cc5afe296.zip
Notes
Notes: svn path=/vendor/ncftp/dist/; revision=3004 svn path=/vendor/ncftp/1.8.5/; revision=3006; tag=vendor/ncftp/1.8.5
-rw-r--r--usr.bin/ncftp/Blurb66
-rw-r--r--usr.bin/ncftp/Makefile.ORIG287
-rw-r--r--usr.bin/ncftp/README359
-rw-r--r--usr.bin/ncftp/cmds.c2214
-rw-r--r--usr.bin/ncftp/cmds.h134
-rw-r--r--usr.bin/ncftp/cmdtab.c282
-rw-r--r--usr.bin/ncftp/copyright.h30
-rw-r--r--usr.bin/ncftp/defaults.h130
-rw-r--r--usr.bin/ncftp/ftp.c1880
-rw-r--r--usr.bin/ncftp/ftp.h67
-rw-r--r--usr.bin/ncftp/ftprc.c612
-rw-r--r--usr.bin/ncftp/ftprc.h39
-rw-r--r--usr.bin/ncftp/getpass.c160
-rw-r--r--usr.bin/ncftp/getpass.h23
-rw-r--r--usr.bin/ncftp/glob.c655
-rw-r--r--usr.bin/ncftp/glob.h22
-rw-r--r--usr.bin/ncftp/main.c1128
-rw-r--r--usr.bin/ncftp/main.h50
-rw-r--r--usr.bin/ncftp/ncftp.11393
-rw-r--r--usr.bin/ncftp/open.c638
-rw-r--r--usr.bin/ncftp/open.h50
-rw-r--r--usr.bin/ncftp/patchlevel.h127
-rw-r--r--usr.bin/ncftp/set.c367
-rw-r--r--usr.bin/ncftp/set.h46
-rw-r--r--usr.bin/ncftp/sys.h611
-rw-r--r--usr.bin/ncftp/tips.c147
-rw-r--r--usr.bin/ncftp/util.c922
-rw-r--r--usr.bin/ncftp/util.h104
-rw-r--r--usr.bin/ncftp/v2_Note35
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).