/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
ftp/cmds.c
2 715 строк
51 KB
Collin Funk
ftp: assume char is 8 bits
13 апр 2026, 01:41
13 апр 2026, 01:41
1655856
Код
Авторство
О чём код?
/* Copyright (C) 1995-2026 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. */ /* * Copyright (c) 1985, 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * FTP User Program -- Command Routines. */ #include <config.h> #include <sys/param.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/ftp.h> #include <ctype.h> #include <error.h> #include <errno.h> #include <netdb.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> /* intmax_t */ #include <string.h> #include <sys/time.h> #include <time.h> #include <unistd.h> /* Include glob.h last, because it may define "const" which breaks system headers on some platforms. */ #include <glob.h> #ifdef HAVE_READLINE_READLINE_H # include <readline/readline.h> #elif defined HAVE_EDITLINE_READLINE_H # include <editline/readline.h> #endif #ifdef HAVE_READLINE_HISTORY_H # include <readline/history.h> #elif defined HAVE_EDITLINE_HISTORY_H # include <editline/history.h> #endif #include "ftp_var.h" #include "attribute.h" #include "xalloc.h" #include "xgetcwd.h" #ifndef DEFPORT # ifdef IPPORT_FTP # define DEFPORT IPPORT_FTP # else/* !IPPORT_FTP */ # define DEFPORT 21 # endif #endif /* !DEFPORT */ /* Returns true if STR is entirely lower case. */ static int all_lower (char *str) { while (*str) if (isupper (*str++)) return 0; return 1; } /* Returns true if STR is entirely upper case. */ static int all_upper (char *str) { while (*str) if (islower (*str++)) return 0; return 1; } /* Destructively converts STR to lower case. */ static char * strdown (char *str) { char *p; for (p = str; *p; p++) if (isupper (*p)) *p = tolower (*p); return str; } jmp_buf jabort; char *mname; char *home = "/"; char *mapin = 0; char *mapout = 0; /* * `Another' gets another argument, and stores the new argc and argv. * It reverts to the top level (via main.c's intr()) on EOF/error. * * Returns false if no new arguments have been added. */ int another (int *pargc, char ***pargv, const char *prompt) { char *arg = NULL; char *buffer, *new; size_t size = 0, len = strlen (line); int ret; buffer = (char *) malloc (sizeof (char) * (strlen (prompt) + 4)); if (!buffer) intr (0); sprintf (buffer, "(%s) ", prompt); #if HAVE_READLINE if (usereadline) arg = readline (buffer); else #endif /* HAVE_READLINE */ { char *nl; fprintf (stdout, "%s", buffer); fflush (stdout); if (getline (&arg, &size, stdin) <= 0) { free (buffer); free (arg); intr (0); } nl = strchr (arg, '\n'); if (nl) *nl = '\0'; } free (buffer); #if HAVE_READLINE if (usereadline && arg && *arg) add_history (arg); #endif /* HAVE_READLINE */ if (!arg) intr (0); else if (!*arg) { free (arg); return 0; } new = realloc (line, sizeof (char) * ((linelen ? linelen : len) + strlen (arg) + 2)); if (!new) { free (arg); intr (0); } line = new; linelen = sizeof (char) * ((linelen ? linelen : len) + strlen (arg) + 2); line[len++] = ' '; strcpy (&line[len], arg); free (arg); makeargv (); ret = margc > *pargc; *pargc = margc; *pargv = margv; return (ret); } /* * Connect to peer server and * auto-login, if possible. */ void setpeer (int argc, char **argv) { char *host = NULL; int port; if (connected && command ("NOOP") != COMPLETE) disconnect (0, 0); else if (connected) { printf ("Already connected to %s, use close first.\n", hostname); code = -1; return; } if (argc < 2) { if (hostname) { host = hostname; argc = 2; } else another (&argc, &argv, "to"); } if (argc < 2 || argc > 3) { printf ("usage: %s host-name [port]\n", argv[0]); code = -1; return; } if (!host) host = argv[1]; if (argc == 3) { if (isdigit (argv[2][0]) || argv[2][0] == '-') port = atoi (argv[2]); else { struct servent *sp; sp = getservbyname (argv[2], "tcp"); port = (sp) ? ntohs (sp->s_port) : 0; } if (port <= 0 || port > 65535) { printf ("%s: bad port -- %s\n", argv[1], argv[2]); printf ("usage: %s host-name [port]\n", argv[0]); code = -1; return; } } else { struct servent *sp; sp = getservbyname ("ftp", "tcp"); port = (sp) ? ntohs (sp->s_port) : DEFPORT; } /* After hookup(), the global variable `hostname' contains * the canonical host name corresponding to the alias name * contained in HOST. The return value of hookup() is not * NULL only if the server has answered our call. The value * of HOST should be preserved for reporting inside login(), * which also detects a correct stanza of the netrc file. */ if (hookup (host, port)) { int overbose; connected = 1; /* * Set up defaults for FTP. */ strcpy (typename, "ascii"), type = TYPE_A; curtype = TYPE_A; strcpy (formname, "non-print"), form = FORM_N; strcpy (modename, "stream"), mode = MODE_S; strcpy (structname, "file"), stru = STRU_F; strcpy (bytename, "8"), bytesize = 8; if (autologin) login (host); #if (defined unix || defined __unix || defined __unix__) /* * this ifdef is to keep someone form "porting" this to an incompatible * system and not checking this out. This way they have to think about it. */ overbose = verbose; if (debug == 0) verbose = -1; if (command ("SYST") == COMPLETE && overbose) { char *cp, c; cp = strchr (reply_string + 4, ' '); if (cp == NULL) cp = strchr (reply_string + 4, '\r'); if (cp) { if (cp[-1] == '.') cp--; c = *cp; *cp = '\0'; } printf ("Remote system type is %s.\n", reply_string + 4); if (cp) *cp = c; } if (!strncmp (reply_string, "215 UNIX Type: L8", 17)) { if (proxy) unix_proxy = 1; else unix_server = 1; /* * Set type to 0 (not specified by user), * meaning binary by default, but don't bother * telling server. We can use binary * for text files unless changed by the user. */ type = 0; strcpy (typename, "binary"); if (overbose) printf ("Using %s mode to transfer files.\n", typename); } else { if (proxy) unix_proxy = 0; else unix_server = 0; if (overbose && !strncmp (reply_string, "215 TOPS20", 10)) printf ("Remember to set tenex mode when transferring binary files from this machine.\n"); } verbose = overbose; #endif /* (unix || __unix || __unix__) */ } } struct types { char *t_name; char *t_mode; int t_type; char *t_arg; } 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, bytename}, {NULL, NULL, 0, NULL} }; /* * Set transfer type. */ void settype (int argc, char **argv) { struct types *p; int comret; if (argc > 2) { char *sep; printf ("usage: %s [", argv[0]); sep = " "; for (p = types; p->t_name; p++) { printf ("%s%s", sep, p->t_name); sep = " | "; } printf (" ]\n"); code = -1; return; } if (argc < 2) { printf ("Using %s mode to transfer files.\n", typename); code = 0; return; } for (p = types; p->t_name; p++) if (strcmp (argv[1], p->t_name) == 0) break; if (p->t_name == 0) { printf ("%s: unknown mode\n", argv[1]); code = -1; return; } if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) comret = command ("TYPE %s %s", p->t_mode, p->t_arg); else comret = command ("TYPE %s", p->t_mode); if (comret == COMPLETE) { strcpy (typename, p->t_name); curtype = type = p->t_type; } } /* * Internal form of settype; changes current type in use with server * without changing our notion of the type for data transfers. * Used to change to and from ascii for listings. */ void changetype (int newtype, int show) { struct types *p; int comret, oldverbose = verbose; if (newtype == 0) newtype = TYPE_I; if (newtype == curtype) return; if (debug == 0 && show == 0) verbose = 0; for (p = types; p->t_name; p++) if (newtype == p->t_type) break; if (p->t_name == 0) { printf ("ftp: internal error: unknown type %d\n", newtype); return; } if (newtype == TYPE_L && bytename[0] != '\0') comret = command ("TYPE %s %s", p->t_mode, bytename); else comret = command ("TYPE %s", p->t_mode); if (comret == COMPLETE) curtype = newtype; verbose = oldverbose; } char *stype[] = { "type", "", 0 }; /* * Set binary transfer type. */ void setbinary (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { stype[1] = "binary"; settype (2, stype); } /* * Set ascii transfer type. */ void setascii (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { stype[1] = "ascii"; settype (2, stype); } /* * Set tenex transfer type. */ void settenex (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { stype[1] = "tenex"; settype (2, stype); } /* * Set file transfer mode. */ void setftmode (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { printf ("We only support %s mode, sorry.\n", modename); code = -1; } /* * Set file transfer format. */ void setform (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { printf ("We only support %s format, sorry.\n", formname); code = -1; } /* * Set file transfer structure. */ void setstruct (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { printf ("We only support %s structure, sorry.\n", structname); code = -1; } /* * Send a single file. */ void put (int argc, char **argv) { char *cmd, *local, *remote; int loc = 0; if (argc == 2) { argc++; argv[2] = argv[1]; loc++; } if (argc < 2 && !another (&argc, &argv, "local-file")) goto usage; if (argc < 3 && !another (&argc, &argv, "remote-file")) { usage: printf ("usage: %s local-file remote-file\n", argv[0]); code = -1; return; } local = globulize (argv[1]); if (!local) { code = -1; return; } /* * If "globulize" modifies argv[1], and argv[2] is a copy of * the old argv[1], make it a copy of the new argv[1]. */ if (loc) remote = strdup (local); else remote = strdup (argv[2]); cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); if (loc && ntflag) { char *new = dotrans (remote); free (remote); remote = new; } if (loc && mapflag) { char *new = domap (remote); if (new != remote) { free (remote); remote = new; } } sendrequest (cmd, local, remote, strcmp (argv[1], local) != 0 || strcmp (argv[2], remote) != 0); free (local); free (remote); } /* * Send multiple files. */ void mput (int argc, char **argv) { int i; sighandler_t oldintr; int ointer; if (argc < 2 && !another (&argc, &argv, "local-files")) { printf ("usage: %s local-files\n", argv[0]); code = -1; return; } mname = argv[0]; mflag = 1; oldintr = signal (SIGINT, mabort); setjmp (jabort); if (proxy) { char *cp; while ((cp = remglob (argv, 0)) != NULL) { if (*cp == 0) mflag = 0; if (mflag && confirm (argv[0], cp)) { char *tp = cp; if (mcase) { if (all_upper (tp)) tp = strdown (strdup (tp)); } if (ntflag) { char *new = dotrans (tp); if (tp != cp) free (tp); tp = new; } if (mapflag) { char *new = domap (tp); if (new != tp) { if (tp != cp) free (tp); tp = new; } } sendrequest ((sunique) ? "STOU" : "STOR", cp, tp, cp != tp || !interactive); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", "mput")) { mflag++; } interactive = ointer; } if (tp != cp) free (tp); } free (cp); } signal (SIGINT, oldintr); mflag = 0; return; } for (i = 1; i < argc; i++) { char **cpp; glob_t gl; int flags; if (!doglob) { if (mflag && confirm (argv[0], argv[i])) { char *tp = argv[i]; if (ntflag) tp = dotrans (tp); if (mapflag) { char *new = domap (tp); if (new != tp) { if (tp != argv[i]) free (tp); tp = new; } } sendrequest ((sunique) ? "STOU" : "STOR", argv[i], tp, tp != argv[i] || !interactive); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", "mput")) { mflag++; } interactive = ointer; } if (tp != argv[i]) free (tp); } continue; } memset (&gl, 0, sizeof (gl)); flags = GLOB_NOCHECK; #ifdef GLOB_BRACE flags |= GLOB_BRACE; #endif #ifdef GLOB_TILDE flags |= GLOB_TILDE; #endif #ifdef GLOB_QUOTE flags |= GLOB_QUOTE; #endif if (glob (argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { error (0, 0, "%s: not found", argv[i]); globfree (&gl); continue; } for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) { if (mflag && confirm (argv[0], *cpp)) { char *tp = *cpp; if (ntflag) tp = dotrans (tp); if (mapflag) { char *new = domap (tp); if (new != tp) { if (tp != *cpp) free (tp); tp = new; } } sendrequest ((sunique) ? "STOU" : "STOR", *cpp, tp, *cpp != tp || !interactive); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", "mput")) { mflag++; } interactive = ointer; } if (tp != *cpp) free (tp); } } globfree (&gl); } signal (SIGINT, oldintr); mflag = 0; } void reget (int argc, char **argv) { getit (argc, argv, 1, "r+w"); } void get (int argc, char **argv) { getit (argc, argv, 0, restart_point ? "r+w" : "w"); } /* * Receive one file. */ int getit (int argc, char **argv, int restartit, char *mode) { int loc = 0; char *local; if (argc == 2) { argc++; argv[2] = argv[1]; loc++; } if (argc < 2 && !another (&argc, &argv, "remote-file")) goto usage; if (argc < 3 && !another (&argc, &argv, "local-file")) { usage: printf ("usage: %s remote-file [ local-file ]\n", argv[0]); code = -1; return (0); } local = globulize (argv[2]); if (!local) { code = -1; return (0); } if (loc && mcase && all_upper (local)) strdown (local); if (loc && ntflag) { char *new = dotrans (local); free (local); local = new; } if (loc && mapflag) { char *new = domap (local); if (new != local) { free (local); local = new; } } if (restartit) { struct stat stbuf; int ret; ret = stat (local, &stbuf); if (restartit == 1) { if (ret < 0) { error (0, errno, "local: %s", local); free (local); return (0); } restart_point = stbuf.st_size; } else { if (ret == 0) { int overbose; overbose = verbose; if (debug == 0) verbose = -1; if (command ("MDTM %s", argv[1]) == COMPLETE) { int yy, mo, day, hour, min, sec; struct tm *tm; verbose = overbose; sscanf (reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, &day, &hour, &min, &sec); tm = gmtime (&stbuf.st_mtime); tm->tm_mon++; if (tm->tm_year + 1900 > yy) { free (local); return (1); } if ((tm->tm_year + 1900 == yy && tm->tm_mon > mo) || (tm->tm_mon == mo && tm->tm_mday > day) || (tm->tm_mday == day && tm->tm_hour > hour) || (tm->tm_hour == hour && tm->tm_min > min) || (tm->tm_min == min && tm->tm_sec > sec)) { free (local); return (1); } } else { printf ("%s\n", reply_string); verbose = overbose; free (local); return (0); } } } } recvrequest ("RETR", local, argv[1], mode, strcmp (local, argv[2]) != 0); restart_point = 0; free (local); return (0); } void mabort (int signo MAYBE_UNUSED) { int ointer; printf ("\n"); fflush (stdout); if (mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", mname)) { interactive = ointer; longjmp (jabort, 0); } interactive = ointer; } mflag = 0; longjmp (jabort, 0); } /* * Get multiple files. */ void mget (int argc, char **argv) { sighandler_t oldintr; int ointer; char *cp, *tp; if (argc < 2 && !another (&argc, &argv, "remote-files")) { printf ("usage: %s remote-files\n", argv[0]); code = -1; return; } mname = argv[0]; mflag = 1; oldintr = signal (SIGINT, mabort); setjmp (jabort); while ((cp = remglob (argv, proxy)) != NULL) { if (*cp == '\0') { mflag = 0; continue; } if (mflag && confirm (argv[0], cp)) { tp = cp; if (mcase && !all_lower (tp)) tp = strdown (strdup (tp)); if (ntflag) { char *new = dotrans (tp); if (tp != cp) free (tp); tp = new; } if (mapflag) { char *new = domap (tp); if (new != tp) { if (tp != cp) free (tp); tp = new; } } recvrequest ("RETR", tp, cp, "w", tp != cp || !interactive); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", "mget")) { mflag++; } interactive = ointer; } if (tp != cp) free (tp); } free (cp); } signal (SIGINT, oldintr); mflag = 0; } char * remglob (char **argv, int doswitch) { static FILE *ftemp = NULL; static char **args; int buf_len = 0; char *buf = 0; int sofar = 0; int oldverbose, oldhash; int fd; char *cp, *mode; if (!mflag) { if (!doglob) { args = NULL; } else { if (ftemp) { fclose (ftemp); ftemp = NULL; } } return (NULL); } if (!doglob) { if (args == NULL) args = argv; if ((cp = *++args) == NULL) args = NULL; return cp ? strdup (cp) : NULL; } if (ftemp == NULL) { char temp[sizeof PATH_TMP + sizeof "XXXXXX"]; strcpy (temp, PATH_TMP); strcat (temp, "XXXXXX"); #ifdef HAVE_MKSTEMP fd = mkstemp (temp); #else if (mktemp (temp) != NULL) fd = open (temp, O_CREAT | O_EXCL | O_RDWR, 0600); else fd = -1; #endif if (fd < 0) { printf ("unable to create temporary file %s: %s\n", temp, strerror (errno)); return (NULL); } close (fd); oldverbose = verbose, verbose = 0; oldhash = hash, hash = 0; if (doswitch) { pswitch (!proxy); } for (mode = "w"; *++argv != NULL; mode = "a") recvrequest ("NLST", temp, *argv, mode, 0); if (doswitch) { pswitch (!proxy); } verbose = oldverbose; hash = oldhash; ftemp = fopen (temp, "r"); unlink (temp); if (ftemp == NULL) { printf ("can't find list of remote files, oops\n"); return (NULL); } } buf_len = 100; /* Any old size */ buf = malloc (buf_len + 1); sofar = 0; for (;;) { if (!buf) { printf ("malloc failure\n"); return 0; } if (!fgets (buf + sofar, buf_len - sofar, ftemp)) { fclose (ftemp); ftemp = NULL; free (buf); return 0; } sofar = strlen (buf); if (buf[sofar - 1] == '\n') { buf[sofar - 1] = '\0'; return buf; } /* Make more room and read some more... */ buf_len += buf_len; buf = realloc (buf, buf_len); } } char * onoff (int val) { return (val ? "on" : "off"); } /* * Show status. */ void status (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { int i; if (connected) printf ("Connected to %s.\n", hostname); else printf ("Not connected.\n"); printf ("Connection addressing: %s\n", (usefamily == AF_UNSPEC) ? "any" : (usefamily == AF_INET6) ? "IPv6" : "IPv4"); if (!proxy) { pswitch (1); if (connected) { printf ("Connected for proxy commands to %s.\n", hostname); } else { printf ("No proxy connection.\n"); } pswitch (0); } printf ("Mode: %s; Type: %s; Form: %s; Structure: %s\n", modename, typename, formname, structname); printf ("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n", onoff (verbose), onoff (bell), onoff (interactive), onoff (doglob)); printf ("Store unique: %s; Receive unique: %s\n", onoff (sunique), onoff (runique)); printf ("Case: %s; CR stripping: %s\n", onoff (mcase), onoff (crflag)); if (ntflag) { printf ("Ntrans: (in) %s (out) %s\n", ntin, ntout); } else { printf ("Ntrans: off\n"); } if (mapflag) { printf ("Nmap: (in) %s (out) %s\n", mapin, mapout); } else { printf ("Nmap: off\n"); } printf ("Hash mark printing: %s; Use of PORT cmds: %s\n", onoff (hash), onoff (sendport)); printf ("Use of EPRT/EPSV for IPv4: %s\n", onoff (doepsv4)); if (macnum > 0) { printf ("Macros:\n"); for (i = 0; i < macnum; i++) { printf ("\t%s\n", macros[i].mac_name); } } code = 0; } /* * Set beep on cmd completed mode. */ void setbell (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { bell = !bell; printf ("Bell mode %s.\n", onoff (bell)); code = bell; } /* * Turn on packet tracing. */ void settrace (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { trace = !trace; printf ("Packet tracing %s.\n", onoff (trace)); code = trace; } /* * Toggle hash mark printing during transfers. * * Parse multipliers 'k', 'K', 'm', 'M', and * 'g', 'G' to simplify the size step. * * With a numerical argument, hash marking is * made active, and the step size is updated. * Toggle state only in absence of an argument. */ void sethash (int argc MAYBE_UNUSED, char **argv) { char *p = argv[1]; /* P is NULL when no argument was passed with `hash'. */ while (p && isdigit (*p)) p++; if (argv[1] != NULL) sscanf (argv[1], "%d", &hashbytes); /* Apply a multiplier only if a numerical part exists. */ if (argv[1] && isdigit (*argv[1])) { hash = 1; /* Enforce markers. */ switch (*p) { case 'g': case 'G': hashbytes *= 1024 * 1024 * 1024; break; case 'm': case 'M': hashbytes *= 1024 * 1024; break; case 'k': case 'K': hashbytes *= 1024; } } if (hashbytes <= 0) hashbytes = 1024; if (!argv[1]) /* Toggle when argument is absent. */ hash = !hash; printf ("Hash mark printing %s", onoff (hash)); if (hash) printf (" (%d bytes/hash mark)", hashbytes); printf (".\n"); code = hash; } /* * Turn on printing of server echo's. */ void setverbose (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { verbose = !verbose; printf ("Verbose mode %s.\n", onoff (verbose)); code = verbose; } /* * Allow any address family. */ void setipany (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { usefamily = AF_UNSPEC; printf ("Selecting addresses: %s.\n", "any"); code = usefamily; } /* * Restrict to IPv4 addresses. */ void setipv4 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { usefamily = AF_INET; printf ("Selecting addresses: %s.\n", "IPv4"); code = usefamily; } /* * Restrict to IPv6 addresses. */ void setipv6 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { usefamily = AF_INET6; printf ("Selecting addresses: %s.\n", "IPv6"); code = usefamily; } /* * Toggle use of EPRT/EPRT for IPv4. */ void setepsv4 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { doepsv4 = !doepsv4; printf ("Use of EPRT/EPSV for IPv4: %s.\n", onoff (doepsv4)); code = doepsv4; } /* * Toggle PORT cmd use before each data connection. */ void setport (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { sendport = !sendport; printf ("Use of PORT cmds %s.\n", onoff (sendport)); code = sendport; } /* * Turn on interactive prompting * during mget, mput, and mdelete. */ void setprompt (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { interactive = !interactive; printf ("Interactive mode %s.\n", onoff (interactive)); code = interactive; } /* * Toggle metacharacter interpretation * on local file names. */ void setglob (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { doglob = !doglob; printf ("Globbing %s.\n", onoff (doglob)); code = doglob; } /* * Set debugging mode on/off and/or * set level of debugging. */ void setdebug (int argc, char **argv) { int val; if (argc > 1) { val = atoi (argv[1]); if (val < 0) { printf ("%s: bad debugging value.\n", argv[1]); code = -1; return; } } else val = !debug; debug = val; if (debug) options |= SO_DEBUG; else options &= ~SO_DEBUG; printf ("Debugging %s (debug=%d).\n", onoff (debug), debug); code = debug > 0; } /* * Set current working directory * on remote machine. */ void cd (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "remote-directory")) { printf ("usage: %s remote-directory\n", argv[0]); code = -1; return; } if (command ("CWD %s", argv[1]) == ERROR && code == 500) { if (verbose) printf ("CWD command not recognized, trying XCWD\n"); command ("XCWD %s", argv[1]); } } /* * Set current working directory * on local machine. */ void lcd (int argc, char **argv) { char *dir; if (argc < 2) argc++, argv[1] = home; if (argc != 2) { printf ("usage: %s local-directory\n", argv[0]); code = -1; return; } dir = globulize (argv[1]); if (!dir) { code = -1; return; } if (chdir (dir) < 0) { error (0, errno, "dir: %s", dir); free (dir); code = -1; return; } free (dir); dir = xgetcwd (); if (dir) { printf ("Local directory now %s\n", dir); free (dir); } else error (0, errno, "getcwd"); code = 0; } /* * Delete a single file. */ void delete (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "remote-file")) { printf ("usage: %s remote-file\n", argv[0]); code = -1; return; } command ("DELE %s", argv[1]); } /* * Delete multiple files. */ void mdelete (int argc, char **argv) { sighandler_t oldintr; int ointer; char *cp; if (argc < 2 && !another (&argc, &argv, "remote-files")) { printf ("usage: %s remote-files\n", argv[0]); code = -1; return; } mname = argv[0]; mflag = 1; oldintr = signal (SIGINT, mabort); setjmp (jabort); while ((cp = remglob (argv, 0)) != NULL) { if (*cp == '\0') { mflag = 0; continue; } if (mflag && confirm (argv[0], cp)) { command ("DELE %s", cp); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", "mdelete")) { mflag++; } interactive = ointer; } } free (cp); } signal (SIGINT, oldintr); mflag = 0; } /* * Rename a remote file. */ void renamefile (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "from-name")) goto usage; if (argc < 3 && !another (&argc, &argv, "to-name")) { usage: printf ("%s from-name to-name\n", argv[0]); code = -1; return; } if (command ("RNFR %s", argv[1]) == CONTINUE) command ("RNTO %s", argv[2]); } /* * Get a directory listing * of remote files. */ void ls (int argc, char **argv) { char *cmd, *dest; if (argc < 2) argc++, argv[1] = NULL; if (argc < 3) argc++, argv[2] = "-"; if (argc > 3) { printf ("usage: %s remote-directory local-file\n", argv[0]); code = -1; return; } cmd = argv[0][0] == 'n' ? "NLST" : "LIST"; if (strcmp (argv[2], "-") != 0) { dest = globulize (argv[2]); if (!dest) { code = -1; return; } if (*dest != '|' && !confirm ("output to local-file:", dest)) { code = -1; goto out; } } else dest = 0; recvrequest (cmd, dest ? dest : "-", argv[1], "w", 0); out: free (dest); } /* * Get a directory listing * of multiple remote files. */ void mls (int argc, char **argv) { sighandler_t oldintr; int ointer, i; char *cmd, mode[1], *dest; if (argc < 2 && !another (&argc, &argv, "remote-files")) goto usage; if (argc < 3 && !another (&argc, &argv, "local-file")) { usage: printf ("usage: %s remote-files local-file\n", argv[0]); code = -1; return; } dest = argv[argc - 1]; argv[argc - 1] = NULL; if (strcmp (dest, "-") && *dest != '|') { dest = globulize (dest); if (!dest) { code = -1; return; } if (!confirm ("output to local-file:", dest)) { code = -1; free (dest); return; } } else dest = strdup (dest); cmd = argv[0][1] == 'l' ? "NLST" : "LIST"; mname = argv[0]; mflag = 1; oldintr = signal (SIGINT, mabort); setjmp (jabort); for (i = 1; mflag && i < argc - 1; ++i) { *mode = (i == 1) ? 'w' : 'a'; recvrequest (cmd, dest, argv[i], mode, 0); if (!mflag && fromatty) { ointer = interactive; interactive = 1; if (confirm ("Continue with", argv[0])) { mflag++; } interactive = ointer; } } signal (SIGINT, oldintr); mflag = 0; free (dest); } /* * Do a shell escape */ void shell (int argc, char **argv MAYBE_UNUSED) { pid_t pid; sighandler_t old1, old2; char shellnam[40], *shell, *namep; old1 = signal (SIGINT, SIG_IGN); old2 = signal (SIGQUIT, SIG_IGN); if ((pid = fork ()) == 0) { for (pid = 3; pid < 20; pid++) close (pid); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); shell = getenv ("SHELL"); if (shell == NULL) shell = PATH_BSHELL; namep = strrchr (shell, '/'); if (namep == NULL) namep = shell; /* No slash in this name. */ else namep++; /* Skip the slash character. */ strcpy (shellnam, "-"); strncat (shellnam, namep, sizeof (shellnam) - 2); if (strcmp (namep, "sh") != 0) shellnam[0] = '+'; if (debug) { printf ("%s\n", shell); fflush (stdout); } if (argc > 1) { execl (shell, shellnam, "-c", altarg, (char *) 0); } else { execl (shell, shellnam, (char *) 0); } error (0, errno, "shell"); code = -1; exit (EXIT_FAILURE); } if (pid > 0) while (wait (0) != pid) ; signal (SIGINT, old1); signal (SIGQUIT, old2); if (pid == -1) { error (0, errno, "Try again later"); code = -1; } else { code = 0; } } /* * Send new user information (re-login) */ void user (int argc, char **argv) { char acct[80]; int n, aflag = 0; if (argc < 2) another (&argc, &argv, "username"); if (argc < 2 || argc > 4) { printf ("usage: %s username [password] [account]\n", argv[0]); code = -1; return; } n = command ("USER %s", argv[1]); if (n == CONTINUE) { /* Is this a case of challenge-response? * RFC 2228 stipulates code 336 for this. * Suppress message in verbose mode, since * it has already been displayed. */ if (code == 336 && !verbose) printf ("%s\n", reply_string + strlen ("336 ")); /* In addition, any password given on the * command line is irrelevant, so ignore it. */ if (argc < 3 || code == 336) argv[2] = getpass ("Password: "); if (argc < 3) argc++; n = command ("PASS %s", argv[2]); if (argv[2]) memset (argv[2], 0, strlen (argv[2])); } if (n == CONTINUE) { if (argc < 4) { printf ("Account: "); fflush (stdout); if (fgets (acct, sizeof (acct) - 1, stdin)) acct[strlen (acct) - 1] = '\0'; /* Erase newline. */ else acct[0] = '\0'; /* Set empty name. */ argv[3] = acct; argc++; } n = command ("ACCT %s", argv[3]); aflag++; } if (n != COMPLETE) { fprintf (stdout, "Login failed.\n"); return; } if (!aflag && argc == 4) { command ("ACCT %s", argv[3]); } } /* * Print working directory. */ void pwd (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { int oldverbose = verbose; /* * If we aren't verbose, this doesn't do anything! */ verbose = 1; if (command ("PWD") == ERROR && code == 500) { printf ("PWD command not recognized, trying XPWD\n"); command ("XPWD"); } verbose = oldverbose; } /* * Print local working directory. */ void lpwd (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { char *dir = xgetcwd (); if (dir) { printf ("Local directory is %s\n", dir); free (dir); } else error (0, errno, "getcwd"); code = 0; } /* * Make a directory. */ void makedir (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "directory-name")) { printf ("usage: %s directory-name\n", argv[0]); code = -1; return; } if (command ("MKD %s", argv[1]) == ERROR && code == 500) { if (verbose) printf ("MKD command not recognized, trying XMKD\n"); command ("XMKD %s", argv[1]); } } /* * Remove a directory. */ void removedir (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "directory-name")) { printf ("usage: %s directory-name\n", argv[0]); code = -1; return; } if (command ("RMD %s", argv[1]) == ERROR && code == 500) { if (verbose) printf ("RMD command not recognized, trying XRMD\n"); command ("XRMD %s", argv[1]); } } /* * Send a line, verbatim, to the remote machine. */ void quote (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "command line to send")) { printf ("usage: %s line-to-send\n", argv[0]); code = -1; return; } quote1 ("", argc, argv); } /* * Send a SITE command to the remote machine. The line * is sent verbatim to the remote machine, except that the * word "SITE" is added at the front. */ void site (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "arguments to SITE command")) { printf ("usage: %s line-to-send\n", argv[0]); code = -1; return; } quote1 ("SITE ", argc, argv); } /* * Turn argv[1..argc) into a space-separated string, then prepend initial text. * Send the result as a one-line command and get response. */ void quote1 (char *initial, int argc, char **argv) { int i, len; char buf[BUFSIZ]; /* must be >= sizeof(line) */ strcpy (buf, initial); if (argc > 1) { len = strlen (buf); len += strlen (strcpy (&buf[len], argv[1])); for (i = 2; i < argc; i++) { buf[len++] = ' '; len += strlen (strcpy (&buf[len], argv[i])); } } if (command (buf) == PRELIM) { while (getreply (0) == PRELIM) continue; } } void do_chmod (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "mode")) goto usage; if (argc < 3 && !another (&argc, &argv, "file-name")) { usage: printf ("usage: %s mode file-name\n", argv[0]); code = -1; return; } command ("SITE CHMOD %s %s", argv[1], argv[2]); } void do_umask (int argc, char **argv) { int oldverbose = verbose; verbose = 1; command (argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); verbose = oldverbose; } void site_idle (int argc, char **argv) { int oldverbose = verbose; verbose = 1; command (argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); verbose = oldverbose; } /* * Ask the other side for help. */ void rmthelp (int argc, char **argv) { int oldverbose = verbose; verbose = 1; command (argc == 1 ? "HELP" : "HELP %s", argv[1]); verbose = oldverbose; } /* * Terminate session and exit. */ void quit (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { if (connected) disconnect (0, 0); pswitch (1); if (connected) { disconnect (0, 0); } exit (EXIT_SUCCESS); } /* * Terminate session, but don't exit. */ void disconnect (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { if (!connected) return; command ("QUIT"); if (cout) { fclose (cout); } cout = NULL; connected = 0; data = -1; if (!proxy) { macnum = 0; } } int confirm (char *cmd, char *file) { char input[BUFSIZ]; if (!interactive) return (1); printf ("%s %s? ", cmd, file); fflush (stdout); if (fgets (input, sizeof input, stdin) == NULL) return (0); return (*input != 'n' && *input != 'N'); } void fatal (char *msg) { error (EXIT_FAILURE, 0, "%s", msg); } /* * 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. */ char * globulize (char *cp) { glob_t gl; int flags; if (!doglob) return strdup (cp); flags = GLOB_BRACE | GLOB_NOCHECK | GLOB_TILDE; #ifdef GLOB_QUOTE flags |= GLOB_QUOTE; #endif memset (&gl, 0, sizeof (gl)); if (glob (cp, flags, NULL, &gl) || gl.gl_pathc == 0) { error (0, 0, "%s: not found", cp); globfree (&gl); return (0); } cp = strdup (gl.gl_pathv[0]); globfree (&gl); return cp; } void account (int argc, char **argv) { char acct[50], *ap; if (argc > 1) { ++argv; --argc; strncpy (acct, *argv, sizeof (acct) - 1); acct[sizeof (acct) - 1] = '\0'; while (argc > 1) { --argc; ++argv; strncat (acct, *argv, (sizeof (acct) - 1) - strlen (acct)); } ap = acct; } else { ap = getpass ("Account:"); } command ("ACCT %s", ap); if (ap) memset (ap, 0, strlen (ap)); } jmp_buf abortprox; void proxabort (int sig MAYBE_UNUSED) { if (!proxy) { pswitch (1); } if (connected) { proxflag = 1; } else { proxflag = 0; } pswitch (0); longjmp (abortprox, 1); } void doproxy (int argc, char **argv) { struct cmd *c; sighandler_t oldintr; if (argc < 2 && !another (&argc, &argv, "command")) { printf ("usage: %s command\n", argv[0]); code = -1; return; } c = getcmd (argv[1]); if (c == (struct cmd *) -1) { printf ("?Ambiguous command\n"); fflush (stdout); code = -1; return; } if (c == 0) { printf ("?Invalid command\n"); fflush (stdout); code = -1; return; } if (!c->c_proxy) { printf ("?Invalid proxy command\n"); fflush (stdout); code = -1; return; } if (setjmp (abortprox)) { code = -1; return; } oldintr = signal (SIGINT, proxabort); pswitch (1); if (c->c_conn && !connected) { printf ("Not connected\n"); fflush (stdout); pswitch (0); signal (SIGINT, oldintr); code = -1; return; } (*c->c_handler) (argc - 1, argv + 1); if (connected) { proxflag = 1; } else { proxflag = 0; } pswitch (0); signal (SIGINT, oldintr); } void setcase (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { mcase = !mcase; printf ("Case mapping %s.\n", onoff (mcase)); code = mcase; } void setcr (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { crflag = !crflag; printf ("Carriage Return stripping %s.\n", onoff (crflag)); code = crflag; } void setntrans (int argc, char **argv) { if (argc == 1) { ntflag = 0; printf ("Ntrans off.\n"); code = ntflag; return; } ntflag++; code = ntflag; strncpy (ntin, argv[1], sizeof (ntin) - 1); ntin[sizeof (ntin) - 1] = '\0'; if (argc == 2) { ntout[0] = '\0'; return; } strncpy (ntout, argv[2], sizeof (ntout) - 1); ntout[sizeof (ntout) - 1] = '\0'; } /* NOTE: dotrans() always returns a newly allocated string. */ char * dotrans (char *name) { char *new = xmalloc (strlen (name) + 1); char *cp1, *cp2 = new; size_t i, ostop, found; for (ostop = 0; *(ntout + ostop) && ostop < sizeof (ntout) - 1; ostop++) continue; for (cp1 = name; *cp1; cp1++) { found = 0; for (i = 0; *(ntin + i) && i < sizeof (ntin) - 1; i++) { if (*cp1 == *(ntin + i)) { found++; if (i < ostop) { *cp2++ = *(ntout + i); } break; } } if (!found) { *cp2++ = *cp1; } } *cp2 = '\0'; return (new); } void setpassive (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { passivemode = !passivemode; printf ("Passive mode %s.\n", onoff (passivemode)); code = passivemode; } void setnmap (int argc, char **argv) { char *cp; if (argc == 1) { mapflag = 0; printf ("Nmap off.\n"); code = mapflag; return; } if (argc < 3 && !another (&argc, &argv, "mapout")) { printf ("Usage: %s [mapin mapout]\n", argv[0]); code = -1; return; } mapflag = 1; code = 1; cp = altarg; while (*cp && !isblank (*cp)) cp++; if (proxy) { while (*cp && isblank (*cp)) cp++; altarg = cp; while (*cp && !isblank (*cp)) cp++; } *cp = '\0'; free (mapin); mapin = strdup (altarg); do cp++; while (*cp && isblank (*cp)); free (mapout); mapout = strdup (cp); } static int cp_subst (char **from_p, char **to_p, int *toks, char **tp, char **te, char *tok0, char **buf_p, int *buf_len_p) { int toknum; char *src; size_t src_len; if (*++(*from_p) == '0') { src = tok0; src_len = strlen (tok0); } else if (toks[toknum = **from_p - '1']) { src = tp[toknum]; src_len = te[toknum] - src; } else return 0; if (src_len > strlen ("$2")) { /* This substitution will be longer than the original text. * Allocate a larger buffer and update the cursor, pointing * within the new memory area. */ size_t offset = *to_p - *buf_p; *buf_len_p += src_len - strlen ("$2"); *buf_p = realloc (*buf_p, *buf_len_p); *to_p = *buf_p + offset; } while (src_len--) *(*to_p)++ = *src++; return 1; } /* NOTE: domap() can return a newly allocated string, * but need not do so every time. */ char * domap (char *name) { /* The string `mapout' will have its tokens expanded, * but is essentially the minimal output string. * Some brackets and some alternate strings might * need to be suppressed. */ int buf_len = strlen (mapout) + 1; char *buf = xmalloc (buf_len); char *cp1 = name, *cp2 = mapin; char *tp[9], *te[9]; int i, toks[9], toknum = 0, match = 1; for (i = 0; i < 9; ++i) { toks[i] = 0; } /* Tokenize the input pattern against incoming file name. */ while (match && *cp1 && *cp2) { switch (*cp2) { case '\\': if (*++cp2 != *cp1) { match = 0; } break; case '$': if (*(cp2 + 1) >= '1' && (*cp2 + 1) <= '9') { if (*cp1 != *(++cp2 + 1)) /* Break at delimiter. */ { toks[toknum = *cp2 - '1']++; tp[toknum] = cp1; while (*++cp1 && *(cp2 + 1) != *cp1) ; te[toknum] = cp1; } cp2++; break; } /* Fall through, as '$' must be used verbatim. */ /* FALLTHROUGH */ default: if (*cp2 != *cp1) { match = 0; } break; } if (match && *cp1) { cp1++; } if (match && *cp2) { cp2++; } } if (!match && *cp1) /* last token mismatch */ { toks[toknum] = 0; } /* Back substitute tokens into output template * string `mapout'. All fixed characters were * already accounted for in presetting BUF_LEN. */ cp1 = buf; *cp1 = '\0'; cp2 = mapout; while (*cp2) { match = 0; switch (*cp2) { case '\\': if (*(cp2 + 1)) { *cp1++ = *++cp2; } break; case '[': LOOP: if (*++cp2 == '$' && isdigit (*(cp2 + 1))) { if (cp_subst (&cp2, &cp1, toks, tp, te, name, &buf, &buf_len)) match = 1; } else { while (*cp2 && *cp2 != ',' && *cp2 != ']') { if (*cp2 == '\\') { cp2++; } else if (*cp2 == '$' && isdigit (*(cp2 + 1))) { if (cp_subst (&cp2, &cp1, toks, tp, te, name, &buf, &buf_len)) match = 1; } else if (*cp2) *cp1++ = *cp2++; } if (!*cp2) { printf ("nmap: unbalanced brackets\n"); return (name); } match = 1; cp2--; } if (match) { /* Skip over all alternate text. */ while (*++cp2 && *cp2 != ']') { if (*cp2 == '\\' && *(cp2 + 1)) { cp2++; } } if (!*cp2) { printf ("nmap: unbalanced brackets\n"); return (name); } break; } switch (*++cp2) { case ',': goto LOOP; case ']': break; default: cp2--; goto LOOP; } break; case '$': if (isdigit (*(cp2 + 1))) { if (cp_subst (&cp2, &cp1, toks, tp, te, name, &buf, &buf_len)) match = 1; break; } /* intentional fall through */ default: *cp1++ = *cp2; break; } cp2++; } *cp1 = '\0'; if (!*buf) strcpy (buf, name); return buf; } void setsunique (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { sunique = !sunique; printf ("Store unique %s.\n", onoff (sunique)); code = sunique; } void setrunique (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { runique = !runique; printf ("Receive unique %s.\n", onoff (runique)); code = runique; } /* change directory to parent directory */ void cdup (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { if (command ("CDUP") == ERROR && code == 500) { if (verbose) printf ("CDUP command not recognized, trying XCUP\n"); command ("XCUP"); } } /* restart transfer at specific point */ void restart (int argc, char **argv) { if (argc != 2) printf ("restart: offset not specified\n"); else { restart_point = atoll (argv[1]); printf ("restarting at %jd. %s\n", (intmax_t) restart_point, "execute get, put or append to initiate transfer"); } } /* show remote system type */ void syst (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED) { command ("SYST"); } void macdef (int argc, char **argv) { char *tmp; int c; if (macnum == 16) { printf ("Limit of 16 macros have already been defined\n"); code = -1; return; } if (argc < 2 && !another (&argc, &argv, "macro name")) { printf ("Usage: %s macro_name\n", argv[0]); code = -1; return; } if (interactive) { printf ("Enter macro line by line, terminating it with a null line\n"); } strncpy (macros[macnum].mac_name, argv[1], sizeof (macros[macnum].mac_name) - 1); 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 (tmp < macbuf + sizeof (macbuf)) { if ((c = getchar ()) == EOF) { printf ("macdef:end of file encountered\n"); code = -1; return; } if ((*tmp = c) == '\n') { if (tmp == macros[macnum].mac_start) { macros[macnum++].mac_end = tmp; code = 0; return; } if (*(tmp - 1) == '\0') { macros[macnum++].mac_end = tmp - 1; code = 0; return; } *tmp = '\0'; } tmp++; } while (1) { while ((c = getchar ()) != '\n' && c != EOF) /* LOOP */ ; if (c == EOF || getchar () == '\n') { printf ("Macro not defined - 4k buffer exceeded\n"); code = -1; return; } } } /* * get size of file on remote machine */ void sizecmd (int argc, char **argv) { if (argc < 2 && !another (&argc, &argv, "filename")) { printf ("usage: %s filename\n", argv[0]); code = -1; return; } command ("SIZE %s", argv[1]); } /* * get last modification time of file on remote machine */ void modtime (int argc, char **argv) { int overbose; if (argc < 2 && !another (&argc, &argv, "filename")) { printf ("usage: %s filename\n", argv[0]); code = -1; return; } overbose = verbose; if (debug == 0) verbose = -1; if (command ("MDTM %s", argv[1]) == COMPLETE) { int yy, mo, day, hour, min, sec; sscanf (reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, &day, &hour, &min, &sec); /* might want to print this in local time */ printf ("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1], mo, day, yy, hour, min, sec); } else printf ("%s\n", reply_string); verbose = overbose; } /* * show status on remote machine */ void rmtstatus (int argc, char **argv) { command (argc > 1 ? "STAT %s" : "STAT", argv[1]); } /* * get file if modtime is more recent than current file */ void newer (int argc, char **argv) { if (getit (argc, argv, -1, "w")) printf ("Local file \"%s\" is newer than remote file \"%s\"\n", argv[2], argv[1]); }