📄 cmdftp.c
字号:
#include "cmdftp.h"/*******************************//* ERROR, WARNING, ... STRINGS *//*******************************/char* msg_err[] = { 0, "bad server response", "Usage: cmdftp [OPTION]... HOSTNAME\n" "Try '"#if HAVE_GETOPT_LONG "cmdftp --help"#else "cmdftp -h"#endif "' for more information.", "connection failed", "giving up on login", "unexpected error", "heap allocation failed", "lost working directory", "double SIGINT, aborting", "no usable tmpdir"};char* msg_war[] = { "bye", "cannot open:", "error writing to:", "error reading from:", "transfer not confirmed", "file not found:", "cannot open temp file:", "unknown command:", "missing argument", "ignoring argument", "login failed", "operation failed or incomplete", "missing environment variable:", "resuming connection lost during", "interrupted during", "could not resume", "invalid source:", "invalid target:", "skipping:", "(local)", "(remote)"};#if HAVE_GETOPT_LONGchar* msg_usage = "cmdftp [OPTION]... HOSTNAME\n\n""options: \n"" -h, --help show this help and quit\n"" -v, --version show version and quit\n"" -p, --port N specify the remote TCP port of the server (def:21)\n"" -b, --buffer N specify the size in bytes of the transfer buffer (def:8192)\n"" -a, --attempts N number of manual login attempts\n"" -q, --quiet be (very) quiet\n"" -n, --no-auto disable autologin (~/.netrc)\n"" -m, --no-manual disable manual login\n"" -g, --no-paging disable output paging\n"" -d, --dotfiles do not ignore dot-files in transfers\n"" -P, --no-path disable path in prompt\n";#elsechar* msg_usage = "cmdftp [OPTION]... HOSTNAME\n\n""options: \n"" -h show this help and quit\n"" -v show version and quit\n"" -p N specify the remote TCP port of the server (def:21)\n"" -b N specify the size in bytes of the transfer buffer (def:8192)\n"" -a N number of manual login attempts\n"" -q be (very) quiet\n"" -n disable autologin (~/.netrc)\n"" -m disable manual login\n"" -g disable output paging\n"" -d do not ignore dot-files in transfers\n"" -P disable path in prompt\n";#endifchar* msg_version = PACKAGE_STRING " Copyright (c) 2003-2006 Claudio Fontana";char* msg_intro = "cmdftp comes with ABSOLUTELY NO WARRANTY; this is free software, and you\n""are welcome to redistribute it under certain conditions specified by the\n""GPL (GNU General Public License). Look at the COPYING file for the details.\n""\nType h after the login to get help about internal commands.\n";char* msg_help[] = { "h", "display this help", "l", "switch to local mode, following commands refer to local", "r", "switch to remote mode, following commands refer to remote", "pwd", "prompt working directory", "cd PATH", "change working directory to PATH", "md PATH", "make new directory PATH", "rd PATH", "remove empty directory PATH", "rm MASK", "delete regular files matching MASK, skip directories", "ls", "list current directory contents", "ls MASK", "list files/dirs matching MASK", "dir", "pretty list of current directory", "cp SRC TRG", "copy SRC to TRG. Behaves like /bin/cp -r", "mv SRC TRG", "move SRC to TRG. Behaves like /bin/mv", "u MASK DIR", "upload files/dirs matching MASK into remote DIR (recurs)", "d MASK DIR", "download files/dirs matching MASK into local DIR (recurs)", "dr MASK DIR", "same as above. If local file already exists, resume", "ren MASK FR TO", "replace FR to TO once in all filenames matching MASK", "p FILE", "print contents of the FILE on the terminal", "e FILE", "edit FILE", "q", "quit client", "quit|exit|bye", "aliases for q command", "<TAB>", "tab-completion"};/***********//* GLOBALS *//***********/struct cmdftp_options o = { 0, 21, 8192, 1, 0, 0, 0, 0, 0, 0 }; /* program options */int cmdftp_control = 0; /* control connection */int cmdftp_data = 0; /* data connection */char* user = 0; char* pass = 0;char logging_in = 0; /* user_logging_in? */volatile int transfer_interrupted = TRAN_INTR_NO; /* Why? */struct hostent* server = 0; /* the remote host */struct termios cmdftp_termios; /* terminal data */char* buffer; /* in/out file transf */char cmd_buffer[CMD_BUF_SIZE + 1]; /* in/out commands */char cmd_line[CMD_BUF_SIZE + 1]; /* in line sep */char cmd_userinput[CMD_BUF_SIZE + 1]; /* buf for term in */char* cmd_ptr; /* ->cmd_buffer */char* env[N_ENV]; /* used env vars */char* cwd[2] = { 0 }; /* working dir {l, r} */int mode = CMDFTP_REMOTE; /* current mode {l, r} */struct sigaction sa;char localhost[256] = { 0 }; /* local host name *//*********************************************//* USER COMMANDS and corresponding functions *//*********************************************/char* commands[] = { "bye", "cat", "cd", "cp", "d", "dir", "dr", "e", "exit", "h", "l", "ls", "md", "mkdir", "mv", "p", "pwd", "q", "quit", "r", "rd", "ren", "rename", "rm", "rmdir", "u" };#define N_COMMANDS (sizeof(commands) / sizeof(char*))fun_ptr cmd_functions[N_COMMANDS] = { cmd_quit, cmd_p, cmd_cd, cmd_cp, cmd_d, cmd_dir, cmd_dr, cmd_e, cmd_quit, cmd_h, cmd_l, cmd_ls, cmd_md, cmd_md, cmd_mv, cmd_p, cmd_pwd, cmd_quit, cmd_quit, cmd_r, cmd_rd, cmd_ren, cmd_ren, cmd_rm, cmd_rd, cmd_u };fun_ptr mf[2][13] = { { &local_chdir, &local_mkdir, &local_rmdir, (fun_ptr)local_getcwd, &local_copy, &local_move, &local_unlink, &local_file, (fun_ptr)local_size, &local_fetch_list, &local_fetch_pretty_list, &local_print, &local_edit }, { &remote_chdir, &remote_mkdir, &remote_rmdir, (fun_ptr)remote_getcwd, &remote_copy, &remote_move, &remote_unlink, &remote_file, (fun_ptr)remote_size, &remote_fetch_list, &remote_fetch_pretty_list, &remote_print, &remote_edit }};/************************************//* ERROR, WARNING, OUTPUT FUNCTIONS *//************************************/void cmdftp_err(int code, char* msg) { fprintf(stderr, "cmdftp: %s %s\n", msg_err[code], msg); exit(code); }void cmdftp_war(int code, char* msg) { if (!o.q) fprintf(stderr, "cmdftp: %s %s\n", msg_war[code], msg); }void usage(void) { printf("\n%s\n", msg_usage); }void version(void) { printf("%s\n", msg_version); }void intro(void) { int i; if (o.q) return; for (i = 0; i < 79; i++) fputc('*', stdout); fputc('\n', stdout); version(); printf("\n%s\n", msg_intro); for (i = 0; i < 79; i++) fputc('*', stdout); fputc('\n', stdout);}void print_progress(char* op, char* fn, size_t fn_len, off_t cur_pos, off_t total_size) { int abbrev; char* units[] = { "", "K", "M", "G", "T" }; int u_idx; char line[WIDE_LINE_LEN]; size_t line_len; double progress = total_size > 0 ? (cur_pos * 100.0 / total_size) : 100.0; u_idx = 0; while ((total_size >> 10) >= 10 && u_idx < 4) { total_size >>= 10; cur_pos >>= 10; u_idx++; } snprintf(line, 75, "%s " OFF_FMT "%s/" OFF_FMT "%s (%4.*f%%) ", op, cur_pos, units[u_idx], total_size, units[u_idx], progress < 1.0 ? 2 : 1, progress); line[74] = '\0'; /* just to be sure that*/ line_len = strlen(line); /* line_len <= 74 */ if (line_len + fn_len < 79) { strcpy(line + line_len, fn); } else { sprintf(line + line_len, "[...]%s", (fn + fn_len - (74 - line_len))); } printf("%s\r", line); fflush(stdout);}/***************************************//* MAIN and other high level functions *//***************************************/void print_prompt(void) { if (!o.q) printf("\r%s:%s>", mode == CMDFTP_LOCAL ? localhost : o.hostname, o.P ? "" : cwd[mode]); fflush(stdout);}int main(int argc, char* argv[]) { if (argc < 2) cmdftp_err(CMDFTP_ERR_OPT, ""); getoptions(argc, argv); if (gethostname(localhost, sizeof(localhost)) < 0) strcpy(localhost, "local"); intro(); reset_cmd_buffer(); buffer = my_malloc(o.b); tcgetattr(0, &cmdftp_termios); atexit(&cmdftp_canon_mode); init_signals(); init_temp(); if (!(cmdftp_control = cmdftp_connect(o.p))) cmdftp_err(CMDFTP_ERR_CONN, ""); greeting(); login_procedure(); do_setcwd(CMDFTP_LOCAL); do_setcwd(CMDFTP_REMOTE); while (1) { char* cmd; print_prompt(); if (!dispatch(cmd = readline(1, 1))) return 0; free(cmd); }}#if HAVE_GETOPT_LONGstruct option long_options[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { "attempts", 1, 0, 'a' }, { "port", 1, 0, 'p' }, { "buffer", 1, 0, 'b' }, { "quiet", 1, 0, 'q' }, { "no-manual", 0, 0, 'm' }, { "no-auto", 0, 0, 'n' }, { "no-paging", 0, 0, 'g' }, { "no-path", 0, 0, 'P' }, { "dotfiles", 0, 0, 'd' }, { 0, 0, 0, 0 }};#endifvoid getoptions(int argc, char* argv[]) { int i, rv; while ((i =#if HAVE_GETOPT_LONG getopt_long(argc, argv, "Pa:b:dghmnp:qv", long_options, 0)#else getopt(argc, argv, "Pa:b:dghmnp:qv")#endif ) != -1) { rv = 1; switch (i) { case 'h' : usage(); cleanexit(); case '?' : case ':' : cmdftp_err(CMDFTP_ERR_OPT, ""); case 'v' : version(); cleanexit(); case 'a' : rv = sscanf(optarg, "%hu", &o.a); break; case 'p' : rv = sscanf(optarg, "%hu", &o.p); break; case 'b' : rv = sscanf(optarg, "%hu", &o.b); break; case 'q' : o.q = 1; break; case 'm' : o.m = 1; break; case 'n' : o.n = 1; break; case 'g' : o.g = 1; break; case 'd' : o.d = 1; break; case 'P' : o.P = 1; break; } if (rv != 1) cmdftp_err(CMDFTP_ERR_OPT, ""); } if (optind >= argc) cmdftp_err(CMDFTP_ERR_OPT, ""); o.hostname = my_strdup(argv[optind]); env[CMDFTP_ENV_PAGER] = o.g ? 0 : getenv("PAGER"); env[CMDFTP_ENV_EDITOR] = getenv("EDITOR"); env[CMDFTP_ENV_HOME] = getenv("HOME"); env[CMDFTP_ENV_TMPDIR] = getenv("TMPDIR");}void greeting(void) { if (recv_answer(0, 0, 0) != 220) cmdftp_err(CMDFTP_ERR_SERV, "(greeting)");}/*********************************//* LOGIN AND AUTOLOGIN FUNCTIONS *//*********************************/void login_procedure(void) { int attempt; user = pass = 0; if (!o.n) { if (auto_login()) return; } if (!o.m) { for (attempt = 0; attempt < o.a; attempt++) { if (user) free(user); if (pass) free(pass); user = pass = 0; if (manual_login()) return; cmdftp_war(CMDFTP_WAR_LGIN, ""); } } cmdftp_err(CMDFTP_ERR_LGIN, "");}int login(char* user, char* pass) { int rv = 0; logging_in = 1; snprintf(cmd_buffer, CMD_BUF_SIZE, "USER %s", user); rv = send_command(cmd_buffer, 0); if (rv == 230) return 1; if (rv != 331) return 0; snprintf(cmd_buffer, CMD_BUF_SIZE, "PASS %s", pass); if (send_command(cmd_buffer, 0) == 230) rv = 1; logging_in = 0; return rv;}int manual_login(void) { printf("Username: "); if (!(user = readline(0, 1))) return 0; cmdftp_pwd_start(); printf("Password: "); pass = readline(0, 0); cmdftp_pwd_end(); if (!pass) return 0; return login(user, pass);}int auto_login(void) { FILE* f; char* fname; char line[WIDE_LINE_LEN]; char token[WIDE_LINE_LEN]; int state, rv; char* rc = ".netrc"; state = rv = 0; if (!env[CMDFTP_ENV_HOME]) return 0; f = fopen((fname = fullpath(env[CMDFTP_ENV_HOME], rc)), "r"); free(fname); if (!f) return 0; while((fgets(line, WIDE_LINE_LEN, f))) { char* ptr, *ptr_new, *ptr_tmp; size_t l; l = strlen(line); if (line[l - 1] == '\n') line[l - 1] = 0; if ((ptr = strchr(line, '#'))) if (ptr == line || ptr[-1] != '\\') *ptr = 0; ptr = ptr_new = ptr_tmp = line; tokenize: if ((l = auto_login_next_token(&ptr, token)) == -1) continue; if (l == 0) { if (!state) { if (strcmp(o.hostname, token) == 0) state++; goto tokenize; } break; } else if (l == 1) { if (!state) { state++; goto tokenize; } break; } else if (l == 2) { if (state && !user) user = my_strdup(token); goto tokenize; } else if (l == 3) { if (state && !pass) pass = my_strdup(token); goto tokenize; } } if (user && !pass) pass = my_strdup(""); if (user) rv = login(user, pass); if (!rv) { if (user) { free(user); user = 0; } if (pass) { free(pass); pass = 0; } } return rv;}int auto_login_next_token(char** hay, char* store) {#define AUTO_N_KEYS 4 char* keys[] = { "machine", "default", "login", "password" }; int keys_len[] = { 7, 7, 5, 8 }; char* ptr, *minptr; int i, min; min = -1; minptr = 0; for (i = 0; i < AUTO_N_KEYS; i++) if ((ptr = strstr(*hay, keys[i]))) if (!minptr || ptr < minptr) { minptr = ptr; min = i; } if (min != -1) { *hay = minptr + keys_len[min]; if (min != 1) { if (!isspace(**hay)) { *store = 0; } else { while (isspace(**hay)) (*hay)++; read_token(hay, store); } } } return min;#undef AUTO_N_KEYS}void read_token(char** hay, char* store) { int i, c; for (i = 0; ; i++) { c = **hay; if (!c || isspace(c)) break; if (c == '\\') { int n = 0; (*hay)++; if (!(c = **hay)) break; if (isdigit(c)) { sscanf(*hay, "%3o%n", (unsigned int*)&c, &n); *hay += n - 1; } else if (c == 'x') { (*hay)++; sscanf(*hay, "%2x%n", (unsigned int*)&c, &n); *hay += n - 1; } else { if (c == 'a') c = '\a'; else if (c == 'b') c = '\b'; else if (c == 'f') c = '\f'; else if (c == 'n') c = '\n'; else if (c == 't') c = '\t'; else if (c == 'v') c = '\v'; else { /* get character as if no backslash present, good to escape spaces */ } } } store[i] = c; (*hay)++; } store[i] = 0;}/***********************//* FUNCTION DISPATCHER *//***********************/int dispatch(char* cmd) { int idx; char* argv[4]; if (!cmd) return 0; split_cmd(cmd, argv); if (*argv[0] == 0) return 1; /* the empty, do nothing command */ if ((idx = str_binsearch(argv[0])) == -1) cmdftp_war(CMDFTP_WAR_BADC, cmd); else if (!cmd_functions[idx](&argv[1])) cmdftp_war(CMDFTP_WAR_FAIL, ""); free_cmd(argv); return 1;}/*************************************************//* LOCAL MODE SPECIFIC UTILITY FUNCTIONS: local_ *//*************************************************/int local_chdir(char* arg) { /* watch return value! */ int rv; if ((rv = chdir(arg)) < 0) cmdftp_war(CMDFTP_WAR_LERR, strerror(errno)); return rv;}int local_mkdir(char* arg) { int rv; if (!(rv = (mkdir(arg, 0777) == 0))) cmdftp_war(CMDFTP_WAR_LERR, strerror(errno)); return rv;}int local_rmdir(char* arg) { int rv; if (!(rv = (rmdir(arg) == 0))) cmdftp_war(CMDFTP_WAR_LERR, strerror(errno)); return rv;}char* local_getcwd(void) { return getcwd(cmd_buffer, CMD_BUF_SIZE);}int local_copy(char* target, char* source) { FILE* t, *s; off_t start_pos, total_size; char* csource; char* op; size_t csource_len; int rv = 0; if (!(s = fopen(source, "rb"))) return 0; if ((total_size = local_size(s)) >= 0 && (t = fopen(target, "wb"))) { op = "Copying"; csource = clean_fn(source); csource_len = strlen(csource); start_transfer: start_pos = 0; if (!o.q) print_progress(op, csource, csource_len, start_pos, total_size); while (start_pos < total_size) { size_t toread, bytes; ssize_t written; toread = ((total_size - start_pos) < o.b) ? total_size - start_pos : o.b; if (!(bytes = fread(buffer, 1, toread, s))) break; if ((written = fwrite(buffer, 1, bytes, t)) != bytes) break; start_pos += bytes; if (!o.q) print_progress(op, csource, csource_len, start_pos, total_size); } if (!o.q) fputc('\n', stdout); if (start_pos < total_size) { if (ferror(s)) cmdftp_war(CMDFTP_WAR_READ, source); else if (ferror(t)) cmdftp_war(CMDFTP_WAR_WRIT, target); else if TRANSFER_INTERRUPTED_CHECK("local copy", 0); else { cmdftp_err(CMDFTP_ERR_UNER, strerror(errno)); } } else { rv = 1; } fclose(t); } fclose(s); return rv;}int local_move(char* new, char* old) { int rv; if (!(rv = (rename(old, new) == 0))) cmdftp_war(CMDFTP_WAR_LERR, strerror(errno)); return rv;}int local_unlink(char* name) { int rv; if (!(rv = (unlink(name) == 0))) cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -