Index: cvs/CVSviaFTP.README diff -c /dev/null cvs/CVSviaFTP.README:1.1 *** /dev/null Tue Aug 26 00:02:58 1997 --- cvs/CVSviaFTP.README Sun Aug 11 17:35:53 1996 *************** *** 0 **** --- 1,188 ---- + CVSviaFTP: Automagically update your remote FTP/WWW site + ======================================================== + + IMPORTANT note: This README file is updated, but not very frequently. + For the newest version look at http://www.siber.com/cvs-via-ftp/index.html. + + + CVSviaFTP is a life-saver for Web site designers and administrators who + need to update their site remotely using FTP protocol only. + Here's my story. + + + Why I did it + ------------ + + I am maintaining a 700+ file multilevel WWW directory tree at + http://www.siber.com/ and I only have FTP access to the tree (my + provider took away shell accounts with access to Web sites from all + the customers citing security concerns). + + Since I have a test site on my local computer and a production site on + the remote computer accessible only via FTP, I would have to manually + FTP over all the changes from the test site to the production site, + which is, to put it mildly, painful. + + So I decided to modify revision control system named CVS to make it + update a mirror of the directory tree *via FTP protocol*. + + The strong points of my designs are: + - Communication is minimal: only updated files are sent over FTP + and nothing else. + - New directories are created automatically at the remote site. + Unneeded directories are automatically removed. + - In addition to Update you can do a complete Checkout of the + local directory tree to the remote site. + + + What is CVS + ----------- + + First, you need to know what the CVS is. I don't really have time to + explain it, so let's assume that you know it. + + And if you don't know what it is, you still can get my distribution of CVS + and read papers and man pages on CVS that appear the distribution. + + In short words, CVS is a revision control system that's built on top + of RCS. The good things about it is that it works, it's free, and I + like it. RCS is a standard low-level Revision Control System most + widely used on UNIX and everywhere else. + + + How to Install CVSviaFTP + ------------------------ + + First, download http://www.siber.com/cvs-via-ftp/cvs-1.8.1-via-ftp.tar.gz, + ungzip and untar it. + + Then follow the instructions in the INSTALL file that's a part of + distribution. CVS is installed just like any other GNU software + package. + + + How CVSviaFTP Works + ------------------- + + I modified CVS so that it can do "cvs checkout" and "cvs update" + at the remote site using FTP protocol. + + To use the CVSviaFTP feature, download and install CVSviaFTP + (described above), and then type in your checked out directory: + + cvs update -F ftp_commands_file + + OR + + cvs checkout -F ftp_commands_file + + + After updating your local directory in the file ftp_commands_file you + will get a sequence of FTP commands that upgrades the FTP-accessible + mirror of this local directory. + + That is, CVSviaFTP will not start an FTP session for you, it will just + write out a file that you can later feed to your favorite FTP client. + It was done on purpose: + - Different people like different FTP clients; + - You need to prepend login commands and append + quit or other finishing commands to the sequence produced by CVSviaFTP. + - For real sensitive sites you may want to manually check + FTP command sequence just to be sure everything's OK. + + + How to Integrate It + ------------------- + + This is an example of a C shell script that does + the whole "local commit / remote update" thing: + + #!/bin/csh -x + if ( "$1" != "" ) then + cd ~/html + cvs commit -m "$1" + endif + cd ~/html-dist/html + cvs update -F ftp.out -P -d + cat ~/etc/ftp-start $ftpfile ~/etc/ftp-finish >ftp-comms + ncftp -u gunky.junky.com + + Added CVSviaFTP: do cvs update and cvs checkout + to the remote site using FTP protocol. + Useful for maintaining remote WWW and FTP sites. + See details in CVSviaFTP-README. + Sun May 5 17:38:21 1996 Benjamin J. Lee Integrated changes submitted by Ian Taylor Index: cvs/src/checkout.c diff -c cvs/src/checkout.c:1.1.1.1 cvs/src/checkout.c:1.2 *** cvs/src/checkout.c:1.1.1.1 Tue Jul 30 12:07:39 1996 --- cvs/src/checkout.c Wed Aug 7 13:58:55 1996 *************** *** 75,80 **** --- 75,81 ---- "\t-D date\tCheck out revisions as of date.\n", "\t-d dir\tCheck out into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", + "\t-F file\tWrite out to file FTP commands to perform remote update/checkout\n", NULL }; *************** *** 122,128 **** else { m_type = CHECKOUT; ! valid_options = "ANnk:d:flRpQqcsr:D:j:P"; valid_usage = checkout_usage; } --- 123,129 ---- else { m_type = CHECKOUT; ! valid_options = "ANnk:d:flRpQqcsr:D:j:PF:"; valid_usage = checkout_usage; } *************** *** 170,175 **** --- 171,184 ---- break; case 'P': checkout_prune_dirs = 1; + case 'F': /* VM: FTP support */ + ftp_comm = TRUE; + ftp_file = fopen(optarg, "w"); + if(ftp_file==NULL) { + error(1, 0, "Cannot open FTP command file '%s' for write\n", + optarg); + } + break; break; case 'p': pipeout = 1; *************** *** 345,352 **** --- 354,370 ---- char repository[PATH_MAX]; (void) CVS_MKDIR (where, 0777); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "mkdir %s\n", where); + } if (chdir (where) < 0) error (1, errno, "cannot chdir to %s", where); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(where); + } preload_update_dir = xstrdup (where); where = (char *) NULL; if (!isfile (CVSADM)) *************** *** 358,363 **** --- 376,386 ---- mode_t omask; omask = umask (cvsumask); (void) CVS_MKDIR (repository, 0777); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "mkdir %s\n", repository); + } (void) umask (omask); } *************** *** 398,403 **** --- 421,430 ---- if (chdir (where) < 0) error (1, errno, "cannot chdir to %s", where); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(where); + } preload_update_dir = xstrdup (where); *************** *** 720,725 **** --- 747,756 ---- preload_update_dir = oldupdate; return (1); } + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(repository); + } which = W_REPOS; if (tag != NULL && !tag_validated) { *************** *** 852,862 **** --- 883,902 ---- *slash = '\0'; *slash2 = '\0'; (void) CVS_MKDIR (cp, 0777); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "mkdir %s\n", cp); + } if (chdir (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); return (1); } + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(cp); + } if (!isfile (CVSADM) && strcmp (command_name, "export") != 0) { (void) sprintf (repository, "%s/%s", prepath, path2); *************** *** 880,889 **** --- 920,938 ---- *slash2 = '/'; } (void) CVS_MKDIR (cp, 0777); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "mkdir %s\n", cp); + } if (chdir (cp) < 0) { error (0, errno, "cannot chdir to %s", cp); return (1); + } + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(cp); } return (0); } Index: cvs/src/cvs.h diff -c cvs/src/cvs.h:1.1.1.1 cvs/src/cvs.h:1.3 *** cvs/src/cvs.h:1.1.1.1 Tue Jul 30 12:07:38 1996 --- cvs/src/cvs.h Wed Aug 7 13:58:56 1996 *************** *** 407,413 **** --- 407,418 ---- extern int really_quiet, quiet; extern int use_editor; extern int cvswrite; + extern int ftp_comm; + + /* VM: FTP support */ + extern FILE *ftp_file; extern mode_t cvsumask; + void ftp_cd(char *dir); extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ Index: cvs/src/main.c diff -c cvs/src/main.c:1.1.1.1 cvs/src/main.c:1.3 *** cvs/src/main.c:1.1.1.1 Tue Jul 30 12:07:40 1996 --- cvs/src/main.c Wed Aug 7 13:58:56 1996 *************** *** 80,85 **** --- 80,89 ---- int logoff = FALSE; mode_t cvsumask = UMASK_DFLT; + /* VM: FTP support */ + int ftp_comm = FALSE; + FILE *ftp_file = NULL; + char *CurDir; /* Index: cvs/src/modules.c diff -c cvs/src/modules.c:1.1.1.1 cvs/src/modules.c:1.2 *** cvs/src/modules.c:1.1.1.1 Tue Jul 30 12:07:41 1996 --- cvs/src/modules.c Wed Aug 7 13:58:58 1996 *************** *** 350,355 **** --- 350,359 ---- err++; goto out; } + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(dir); + } if (!isfile (CVSADM)) { char nullrepos[PATH_MAX]; Index: cvs/src/rcscmds.c diff -c cvs/src/rcscmds.c:1.1.1.1 cvs/src/rcscmds.c:1.3 *** cvs/src/rcscmds.c:1.1.1.1 Tue Jul 30 12:07:41 1996 --- cvs/src/rcscmds.c Wed Aug 7 13:58:58 1996 *************** *** 138,143 **** --- 138,155 ---- int flags; int noerr; { + /* VM: FTP support */ + if(ftp_comm) { + static char save_dir_name[MAXPATHLEN] = {0}; + char dir_name[MAXPATHLEN]; + getcwd(dir_name, MAXPATHLEN); + if(strcmp(dir_name, save_dir_name)!=0) { + fprintf(ftp_file, "lcd %s\n", dir_name); + strcpy(save_dir_name, dir_name); + } + ftp_cd(NULL); + fprintf(ftp_file, "put %s\n", workfile); + } run_setup ("%s%s -x,v/ -q %s%s", Rcsbin, RCS_CO, tag ? "-r" : "", tag ? tag : ""); if (options != NULL && options[0] != '\0') Index: cvs/src/recurse.c diff -c cvs/src/recurse.c:1.1.1.1 cvs/src/recurse.c:1.3 *** cvs/src/recurse.c:1.1.1.1 Tue Jul 30 12:07:41 1996 --- cvs/src/recurse.c Wed Aug 7 13:58:59 1996 *************** *** 571,576 **** --- 571,580 ---- /* cd to the sub-directory */ if (chdir (dir) < 0) error (1, errno, "could not chdir to %s", dir); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(dir); + } /* honor the global SKIP_DIRS (a.k.a. local) */ if (flags == R_SKIP_DIRS) *************** *** 684,689 **** --- 688,697 ---- exit (EXIT_FAILURE); if (chdir (p->key) < 0) error (1, errno, "could not chdir to %s", p->key); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(p->key); + } save_update_dir = xstrdup (update_dir); Index: cvs/src/update.c diff -c cvs/src/update.c:1.1.1.1 cvs/src/update.c:1.3 *** cvs/src/update.c:1.1.1.1 Tue Jul 30 12:07:42 1996 --- cvs/src/update.c Wed Aug 7 13:58:59 1996 *************** *** 100,105 **** --- 100,106 ---- "\t-j rev\tMerge in changes made between current revision and rev.\n", "\t-I ign\tMore files to ignore (! to reset).\n", "\t-W spec\tWrappers specification line.\n", + "\t-F file\tWrite out to file FTP commands to perform remote update/checkout\n", NULL }; *************** *** 123,129 **** /* parse the args */ optind = 1; ! while ((c = getopt (argc, argv, "ApPflRQqduk:r:D:j:I:W:")) != -1) { switch (c) { --- 124,130 ---- /* parse the args */ optind = 1; ! while ((c = getopt (argc, argv, "ApPflRQqduk:r:D:j:I:W:F:")) != -1) { switch (c) { *************** *** 136,141 **** --- 137,150 ---- case 'W': wrap_add (optarg, 0); break; + case 'F': /* VM: FTP support */ + ftp_comm = TRUE; + ftp_file = fopen(optarg, "w"); + if(ftp_file==NULL) { + error(1, 0, "Cannot open FTP command file '%s' for write\n", + optarg); + } + break; case 'k': if (options) free (options); *************** *** 738,743 **** --- 747,757 ---- { /* otherwise, create the dir and appropriate adm files */ make_directory (dir); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "mkdir %s\n", dir); + } Create_Admin (dir, update_dir, repository, tag, date); } } *************** *** 845,850 **** --- 859,868 ---- /* FIXME: chdir ("..") loses with symlinks. */ /* Prune empty dirs on the way out - if necessary */ (void) chdir (".."); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(".."); + } if (update_prune_dirs && isemptydir (dir)) { /* I'm not sure the existence_error is actually possible (except *************** *** 852,857 **** --- 870,880 ---- this code used to ignore all errors, I'll play it safe. */ if (unlink_file_dir (dir) < 0 && !existence_error (errno)) error (0, errno, "cannot remove %s directory", dir); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "rmdir %s\n", dir); + } } return (err); *************** *** 899,904 **** --- 922,932 ---- history_write ('W', update_dir, "", file, repository); Scratch_Entry (entries, file); (void) unlink_file (file); + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "delete %s\n", file); + } return (0); } *************** *** 1067,1072 **** --- 1095,1105 ---- error (0, errno, "cannot remove %s/%s", update_dir, file); } + /* VM: FTP support */ + if(ftp_comm) { + ftp_cd(NULL); + fprintf(ftp_file, "delete %s\n", file); + } } else Register (entries, file, *************** *** 1827,1830 **** --- 1860,1958 ---- joining () { return (join_rev1 != NULL); + } + + + /* + * VM: CD in FTP tree. + * The goal is to turn sequence of CDs into one CD, + * thus saving communication time. + * + * Possible dir_name: aaa/bbb/ccc aaa . .. ../aaa/bb + * dir_name==NULL means: output accumulated CD. + * + * We interprete .. here, so don't use symbolic links. + * BTW, FTP does not like symbolic links either. + */ + void ftp_cd(char *dir) { + static char save_dir[MAXPATHLEN] = {0}; + static char curr_dir[MAXPATHLEN] = {0}; + static char *pcdir = curr_dir; + static int updated = TRUE; + static int bad_dir = FALSE; + + if(dir==NULL) { + /* Dump CD if it new curr_dir is different from the last time */ + if(strcmp(curr_dir,save_dir) != 0) { + char tmp_dir[MAXPATHLEN]; + int i; + char *p; + + if(bad_dir) { + error(1, 0, "Attempt to cd to directory above the current directory\n"); + } + + /* Move down from save_dir to root */ + fprintf(ftp_file, "cd ", tmp_dir); + for(p=save_dir; *p; p++) { + if(*p=='/') { + fprintf(ftp_file, "../"); + } + } + + /* Move up to curr_dir */ + strcpy(tmp_dir, curr_dir); + i = strlen(tmp_dir); + if(i>0) { + if(tmp_dir[--i]!='/') { + error(1,0, "ftp_cd: bad directory name: %s\n", tmp_dir); + } + tmp_dir[i]=0; + } + fprintf(ftp_file, "%s\n", tmp_dir); + strcpy(save_dir, curr_dir); + } + + } else { + + /* Parse dir */ + char *p, *q; + for(p=dir; *p; ) { + char name[MAXPATHLEN]; + char *r = &name[0]; + for(q=p; *q && *q!='/'; q++) { + *r++ = *q; + } + if(*q) { + p = q + 1; /* next member name */ + } else { + p = q; + } + *r = 0; + + if(name[0]==0 || strcmp(name, ".") == 0) { + /* Do nothing */ + + } else if(strcmp(name, "..") == 0) { + /* Go up */ + /* + * CVS thinks that "cd ." should be matched by "cd ..". + * So allow it do "cd .." to beyond the root, bit don't let it use it. + */ + if(pcdir=curr_dir && *pcdir!='/'; ) ; + *++pcdir = 0; + + } else { + /* Go down */ + for(r=name; *r; r++) { + *pcdir++ = *r; + } + *pcdir++ = '/'; + *pcdir = 0; + } + } + } }