From c198d4c8be6b1a4f042151714107ff37fc446471 Mon Sep 17 00:00:00 2001 From: Richard Sharpe Date: Fri, 18 Sep 1998 12:47:46 +0000 Subject: [PATCH] Adding rewritten restore code ... Old code is still there surrounded by a OLD_DOTARPUT. (This used to be commit 00ba54d4ee9ad875c5cfbee09d4b745df5f116ab) --- source3/client/client.c | 254 +++++++++++++++++++++++++++++++++++++--- source3/client/clitar.c | 286 ++++++++++++++++++++++++++++++++++++--------- source3/include/includes.h | 25 ++++ 3 files changed, 496 insertions(+), 69 deletions(-) diff --git a/source3/client/client.c b/source3/client/client.c index b48e520d96a..684a2502b54 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -3209,28 +3209,35 @@ static BOOL list_servers(char *wk_grp) } +/* Some constants for completing filename arguments */ + +#define COMPL_NONE 0 /* No completions */ +#define COMPL_REMOTE 1 /* Complete remote filename */ +#define COMPL_LOCAL 2 /* Complete local filename */ + /* This defines the commands supported by this client */ struct { char *name; void (*fn)(char *, char *); char *description; + char compl_args[2]; /* Completion argument info */ } commands[] = { - {"ls",cmd_dir," list the contents of the current directory"}, - {"dir",cmd_dir," list the contents of the current directory"}, - {"lcd",cmd_lcd,"[directory] change/report the local current working directory"}, - {"cd",cmd_cd,"[directory] change/report the remote directory"}, + {"ls",cmd_dir," list the contents of the current directory",COMPL_REMOTE}, + {"dir",cmd_dir," list the contents of the current directory",COMPL_REMOTE}, + {"lcd",cmd_lcd,"[directory] change/report the local current working directory",COMPL_LOCAL}, + {"cd",cmd_cd,"[directory] change/report the remote directory",COMPL_REMOTE}, {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)"}, - {"get",cmd_get," [local name] get a file"}, - {"mget",cmd_mget," get all the matching files"}, - {"put",cmd_put," [remote name] put a file"}, - {"mput",cmd_mput," put all matching files"}, - {"rename",cmd_rename," rename some files"}, - {"more",cmd_more," view a remote file with your pager"}, - {"mask",cmd_select," mask all filenames against this"}, - {"del",cmd_del," delete all matching files"}, - {"rm",cmd_del," delete all matching files"}, + {"get",cmd_get," [local name] get a file",COMPL_REMOTE,COMPL_LOCAL}, + {"mget",cmd_mget," get all the matching files",COMPL_REMOTE}, + {"put",cmd_put," [remote name] put a file",COMPL_LOCAL,COMPL_REMOTE}, + {"mput",cmd_mput," put all matching files",COMPL_REMOTE}, + {"rename",cmd_rename," rename some files",COMPL_REMOTE,COMPL_REMOTE}, + {"more",cmd_more," view a remote file with your pager",COMPL_REMOTE}, + {"mask",cmd_select," mask all filenames against this",COMPL_REMOTE}, + {"del",cmd_del," delete all matching files",COMPL_REMOTE}, + {"rm",cmd_del," delete all matching files",COMPL_REMOTE}, {"mkdir",cmd_mkdir," make a directory"}, {"md",cmd_mkdir," make a directory"}, {"rmdir",cmd_rmdir," remove a directory"}, @@ -3248,13 +3255,13 @@ struct {"quit",cli_send_logout,"logoff the server"}, {"q",cli_send_logout,"logoff the server"}, {"exit",cli_send_logout,"logoff the server"}, - {"newer",cmd_newer," only mget files newer than the specified local file"}, + {"newer",cmd_newer," only mget files newer than the specified local file",COMPL_LOCAL}, {"archive",cmd_archive,"\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit"}, {"tar",cmd_tar,"tar [IXbgNa] current directory to/from " }, {"blocksize",cmd_block,"blocksize (default 20)" }, {"tarmode",cmd_tarmode, " tar's behaviour towards archive bits" }, - {"setmode",cmd_setmode,"filename change modes of file"}, + {"setmode",cmd_setmode,"filename change modes of file",COMPL_REMOTE}, {"help",cmd_help,"[command] give help on a command"}, {"?",cmd_help,"[command] give help on a command"}, {"!",NULL,"run a shell command on the local system"}, @@ -3356,6 +3363,182 @@ static void wait_keyboard(char *buffer) } } +#ifdef HAVE_LIBREADLINE + +/**************************************************************************** + completion routines for GNU Readline +****************************************************************************/ + +/* To avoid filename completion being activated when no valid + completions are found, we assign this stub completion function + to the rl_completion_entry_function variable. */ + +char *complete_cmd_null(char *text, int state) +{ + return NULL; +} + +/* Argh. This is starting to get ugly. We need to be able to pass data + back from the do_dir() iterator function. */ + +static int compl_state; +static char *compl_text; +static pstring result; + +/* Iterator function for do_dir() */ + +void complete_process_file(file_info *f) +{ + /* Do we have a partial match? */ + + if ((compl_state >= 0) && (strncmp(compl_text, f->name, + strlen(compl_text)) == 0)) { + + /* Return filename if we have made enough matches */ + + if (compl_state == 0) { + pstrcpy(result, f->name); + compl_state = -1; + + return; + } + compl_state--; + } +} + +/* Complete a remote file */ + +char *complete_remote_file(char *text, int state) +{ + char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + int attribute = aDIR | aSYSTEM | aHIDDEN; + pstring mask; + + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return(NULL); + + /* Create dir mask */ + + pstrcpy(mask, cur_dir); + pstrcat(mask, "*"); + + /* Initialise static vars for filename match */ + + compl_text = text; + compl_state = state; + result[0] = '\0'; + + /* Iterate over all files in directory */ + + do_dir(InBuffer, OutBuffer, mask, attribute, complete_process_file, False, + True); + + /* Clean up */ + + free(InBuffer); + free(OutBuffer); + + /* Return matched filename */ + + if (result[0] != '\0') { + return strdup(result); /* Readline will dispose of strings */ + } else { + return NULL; + } +} + +/* Complete a smbclient command */ + +char *complete_cmd(char *text, int state) +{ + static int cmd_index; + char *name; + + /* Initialise */ + + if (state == 0) { + cmd_index = 0; + } + + /* Return the next name which partially matches the list of commands */ + + while (strlen(name = commands[cmd_index++].name) > 0) { + if (strncmp(name, text, strlen(text)) == 0) { + return strdup(name); + } + } + + return NULL; +} + +/* Main completion function for smbclient. Work out which word we are + trying to complete and call the appropriate function. */ + +char **completion_fn(char *text, int start, int end) +{ + int i, num_words, cmd_index; + char lastch = ' '; + + /* If we are at the start of a word, we are completing a smbclient + command. */ + + if (start == 0) { + return completion_matches(text, complete_cmd); + } + + /* Count # of words in command */ + + num_words = 0; + for (i = 0; i <= end; i++) { + if ((rl_line_buffer[i] != ' ') && (lastch == ' ')) + num_words++; + lastch = rl_line_buffer[i]; + } + + if (rl_line_buffer[end] == ' ') + num_words++; + + /* Work out which command we are completing for */ + + for (cmd_index = 0; strcmp(commands[cmd_index].name, "") != 0; + cmd_index++) { + + /* Check each command in array */ + + if (strncmp(rl_line_buffer, commands[cmd_index].name, + strlen(commands[cmd_index].name)) == 0) { + + /* Call appropriate completion function */ + + if ((num_words == 2) || (num_words == 3)) { + switch (commands[cmd_index].compl_args[num_words - 2]) { + + case COMPL_REMOTE: + return completion_matches(text, complete_remote_file); + break; + + case COMPL_LOCAL: + return completion_matches(text, filename_completion_function); + break; + + default: + /* An invalid completion type */ + break; + } + } + + /* We're either completing an argument > 3 or found an invalid + completion type. Either way do nothing about it. */ + + break; + } + } + + return NULL; +} + +#endif /* HAVE_LIBREADLINE */ /**************************************************************************** process commands from the client @@ -3422,6 +3605,30 @@ static BOOL process(char *base_directory) bzero(OutBuffer,smb_size); +#ifdef HAVE_LIBREADLINE + + { + pstring prompt; + + /* Read input using GNU Readline */ + + slprintf(prompt, sizeof(prompt) - 1, "smb: %s> ", CNV_LANG(cur_dir)); + if (!readline(prompt)) + break; + + /* Copy read line to samba buffer */ + + pstrcpy(line, rl_line_buffer); + pstrcat(line, "\n"); + + /* Add line to history */ + + if (strlen(line) > 0) + add_history(line); + } + +#else + /* display a prompt */ DEBUG(0,("smb: %s> ", CNV_LANG(cur_dir))); dbgflush( ); @@ -3432,6 +3639,8 @@ static BOOL process(char *base_directory) if (!fgets(line,1000,stdin)) break; +#endif + /* input language code to internal one */ CNV_INPUT (line); @@ -3755,6 +3964,19 @@ static void usage(char *pname) exit(1); } +#ifdef HAVE_LIBREADLINE + + /* Initialise GNU Readline */ + + rl_readline_name = "smbclient"; + rl_attempted_completion_function = completion_fn; + rl_completion_entry_function = (Function *)complete_cmd_null; + + /* Initialise history list */ + + using_history(); + +#endif /* HAVE_LIBREADLINE */ DEBUG( 3, ( "Client started (version %s).\n", VERSION ) ); @@ -3848,5 +4070,3 @@ static void usage(char *pname) return(0); } - - diff --git a/source3/client/clitar.c b/source3/client/clitar.c index 1ca8c1a4dcc..c38f9fdec9c 100644 --- a/source3/client/clitar.c +++ b/source3/client/clitar.c @@ -85,7 +85,7 @@ static int attribute = aDIR | aSYSTEM | aHIDDEN; #define CLIENT_TIMEOUT (30*1000) #endif -static char *tarbuf; +static char *tarbuf, *buffer_p; static int tp, ntarf, tbufsiz, ttarf; /* Incremental mode */ BOOL tar_inc=False; @@ -1554,18 +1554,26 @@ static void unfixtarname(char *tptr, char *fp, int l, BOOL first) } } -#if 0 /* Removed to get around gcc 'defined but not used' error. */ +#ifndef OLD_DOTARPUT /**************************************************************************** Move to the next block in the buffer, which may mean read in another set of -blocks. +blocks. FIXME, we should allow more than one block to be skipped. ****************************************************************************/ -static int next_block(char *ltarbuf, char *bufferp, int bufsiz) +static int next_block(char *ltarbuf, char **bufferp, int bufsiz) { int bufread, total = 0; - if (bufferp >= (ltarbuf + bufsiz)) { - + DEBUG(5, ("Advancing to next block: %0X\n", *bufferp)); + *bufferp += TBLOCK; + total = TBLOCK; + + if (*bufferp >= (ltarbuf + bufsiz)) { + + DEBUG(5, ("Reading more data into ltarbuf ...\n")); + + total = 0; + for (bufread = read(tarhandle, ltarbuf, bufsiz); total < bufsiz; total += bufread) { if (bufread <= 0) { /* An error, return false */ @@ -1574,49 +1582,192 @@ static int next_block(char *ltarbuf, char *bufferp, int bufsiz) } - bufferp = ltarbuf; - - } - else { + DEBUG(5, ("Total bytes read ... %i\n", total)); - bufferp += TBLOCK; + *bufferp = ltarbuf; } - return(0); + return(total); } -static int skip_file(int skip) +/* Skip a file, even if it includes a long file name? */ +static int skip_file(int skipsize) { + int dsize = skipsize; - return(0); + DEBUG(5, ("Skiping file. Size = %i\n", skipsize)); + + /* FIXME, we should skip more than one block at a time */ + + while (dsize > 0) { + + if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { + + DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); + return(False); + + } + + dsize -= TBLOCK; + + } + + return(True); } -static int get_file(file_info2 finfo) +/* We get a file from the tar file and store it */ +static int get_file(file_info2 finfo, char * inbuf, char * outbuf) { + int fsize = finfo.size; + int fnum, pos = 0, dsize = 0, wsize = 0, rsize = 0; - return(0); + DEBUG(5, ("get_file: file: %s, size %i\n", finfo.name, fsize)); + + if (ensurepath(finfo.name, inbuf, outbuf) && + !smbcreat(finfo, &fnum, inbuf, outbuf)) + { + DEBUG(0, ("abandoning restore\n")); + return(False); + } + + DEBUG(0, ("restore tar file %s of size %d bytes\n", + finfo.name, finfo.size)); + + /* read the blocks from the tar file and write to the remote file */ + + rsize = fsize; + + while (rsize > 0) { + + dsize = MIN(tbufsiz - (buffer_p - tarbuf), rsize); /* Calculate the size to write */ + + DEBUG(5, ("writing %i bytes ...\n", dsize)); + + if (!smbwrite(fnum, dsize, pos, 0, fsize - pos, buffer_p, inbuf, outbuf)) { + + DEBUG(0, ("Error writing remote file\n")); + return 0; + + } + + rsize -= dsize; + + /* Now figure out how much to move in the buffer */ + + /* FIXME, we should skip more than one block at a time */ + + while (dsize > 0) { + + if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { + + DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); + return False; + + } + + dsize -= TBLOCK; + + } + + } + + /* Now close the file ... */ + + if (!smbshut(finfo, fnum, inbuf, outbuf)) { + + DEBUG(0, ("Error closing remote file\n")); + return(False); + + } + + /* Now we update the creation date ... */ + + DEBUG(5, ("Updating creation date on %s\n", finfo.name)); + + if (!do_setrtime(finfo.name, finfo.mtime)) { + + DEBUG(0, ("Could not set time on file: %s\n", finfo.name)); + /*return(False); */ /* Ignore, as Win95 does not allow changes */ + + } + + ntarf++; + return(True); } -static int get_dir(file_info2 finfo) +/* Create a directory. We just ensure that the path exists and return as there + is no file associated with a directory +*/ +static int get_dir(file_info2 finfo, char * inbuf, char * outbuf) { - return(0); + DEBUG(5, ("Creating directory: %s\n", finfo.name)); -} + if (!ensurepath(finfo.name, inbuf, outbuf)) { + + DEBUG(0, ("Problems creating directory\n")); + return(False); + + } + return(True); +} +/* Get a file with a long file name ... first file has file name, next file + has the data. We only want the long file name, as the loop in do_tarput + will deal with the rest. +*/ static char * get_longfilename(file_info2 finfo) { + int namesize = finfo.size + strlen(cur_dir) + 2; + char *longname = malloc(namesize); + char *xxx; + int offset = 0, left = finfo.size; - return(NULL); + DEBUG(5, ("Restoring a long file name: %s\n", finfo.name)); + DEBUG(5, ("Len = %i\n", finfo.size)); + fflush(stderr); -} + if (longname == NULL) { + + DEBUG(0, ("could not allocate buffer of size %d for longname\n", + finfo.size + strlen(cur_dir) + 2)); + return(NULL); + } + + /* First, add cur_dir to the long file name */ + + if (strlen(cur_dir) > 0) { + strncpy(longname, cur_dir, namesize); + offset = strlen(cur_dir); + } + + /* Loop through the blocks picking up the name */ + + while (left > 0) { + + if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { + + DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); + return(NULL); + + } + + unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size)); + DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p)); -static char * bufferp; + offset += TBLOCK; + left -= TBLOCK; -static void do_tarput2(void) + } + + return(longname); + +} + +static void do_tarput(void) { file_info2 finfo, *finfo2; struct timeval tp_start; @@ -1625,8 +1776,11 @@ static void do_tarput2(void) GetTimeOfDay(&tp_start); - bufferp = tarbuf + tbufsiz; /* init this to force first read */ + DEBUG(5, ("RJS do_tarput called ...\n")); + + buffer_p = tarbuf + tbufsiz; /* init this to force first read */ +#ifdef 0 /* Fix later ... */ if (push_dir(&dir_stack, &finfo)) { finfo2 = pop_dir(&dir_stack); @@ -1637,6 +1791,7 @@ static void do_tarput2(void) } } +#endif inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); @@ -1648,21 +1803,27 @@ static void do_tarput2(void) } - if (next_block(tarbuf, bufferp, tbufsiz) <= 0) { + /* Now read through those files ... */ + + while (True) { - DEBUG(0, ("Empty file or short tar file: %s\n", strerror(errno))); + /* Get us to the next block, or the first block first time around */ - } + if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) { - /* Now read through those files ... */ + DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno))); - while (True) { + return; + + } - switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir)) { + DEBUG(5, ("Reading the next header ...\n")); + fflush(stdout); + switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) { case -2: /* Hmm, not good, but not fatal */ DEBUG(0, ("Skipping %s...\n", finfo.name)); - if ((next_block(tarbuf, bufferp, tbufsiz) <= 0) && + if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) { DEBUG(0, ("Short file, bailing out...\n")); @@ -1691,46 +1852,65 @@ static void do_tarput2(void) /* Now, do we have a long file name? */ if (longfilename != NULL) { - if (strlen(longfilename) < sizeof(finfo.name)) { /* if we have space */ - strncpy(finfo.name, longfilename, sizeof(finfo.name) - 1); - free(longfilename); - longfilename = NULL; + free(finfo.name); /* Free the space already allocated */ + finfo.name = longfilename; + longfilename = NULL; - } - else { + } - DEBUG(0, ("filename: %s too long, skipping\n", strlen(longfilename))); - skip = True; + /* Well, now we have a header, process the file ... */ - } - } + /* Should we skip the file? We have the long name as well here */ - /* Well, now we have a header, process the file ... */ + skip = clipn && + ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) +#ifdef HAVE_REGEX_H + || (tar_re_search && !regexec(preg, finfo.name, 0, NULL, 0))); +#else + || (tar_re_search && mask_match(finfo.name, cliplist[0], True, False))); +#endif - /* Should we skip the file? */ + DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, cliplist[0], finfo.name)); - if (skip) { + if (skip) { - skip_file(finfo.size); - continue; + skip_file(finfo.size); + continue; - } + } /* We only get this far if we should process the file */ - switch (((union hblock *)bufferp) -> dbuf.linkflag) { + switch (((union hblock *)buffer_p) -> dbuf.linkflag) { case '0': /* Should use symbolic names--FIXME */ - get_file(finfo); + if (!get_file(finfo, inbuf, outbuf)) { + + free(inbuf); free(outbuf); + DEBUG(0, ("Abandoning restore\n")); + return; + + } break; case '5': - get_dir(finfo); + if (!get_dir(finfo, inbuf, outbuf)) { + free(inbuf); free(outbuf); + DEBUG(0, ("Abandoning restore \n")); + return; + } break; case 'L': longfilename = get_longfilename(finfo); + if (!longfilename) { + free(inbuf); free(outbuf); + DEBUG(0, ("abandoning restore\n")); + return; + + } + DEBUG(5, ("Long file name: %s\n", longfilename)); break; default: @@ -1743,7 +1923,8 @@ static void do_tarput2(void) } -#endif /* Removed to get around gcc 'defined but not used' error. */ + +#else static void do_tarput() { @@ -1774,7 +1955,7 @@ static void do_tarput() /* These should be the only reads in clitar.c */ while ((bufread=read(tarhandle, tarbuf, tbufsiz))>0) { - char *buffer_p, *endofbuffer; + char *endofbuffer; int chunk; /* Code to handle a short read. @@ -2062,6 +2243,7 @@ static void do_tarput() free(inbuf); free(outbuf); } +#endif /* * samba interactive commands diff --git a/source3/include/includes.h b/source3/include/includes.h index 62c044603d7..8c8ee32301d 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -611,6 +611,31 @@ union semun { #define ULTRIX_AUTH 1 #endif +/* This is the naughty bit. Autoconf should declare these symbols if + it finds that GNU Readline is installed. */ + +#define HAVE_LIBREADLINE +#define HAVE_READLINE_READLINE_H +#define HAVE_READLINE_HISTORY_H + +#ifdef HAVE_LIBREADLINE +# ifdef HAVE_READLINE_READLINE_H +# include +# ifdef HAVE_READLINE_HISTORY_H +# include +# endif +# else +# ifdef HAVE_READLINE_H +# include +# ifdef HAVE_HISTORY_H +# include +# endif +# else +# undef HAVE_LIBREADLINE +# endif +# endif +#endif + #ifndef HAVE_STRDUP char *strdup(const char *s); #endif -- 2.11.4.GIT