remsync.c

Go to the documentation of this file.
00001 /*      remsync 1.5 - remotely synchronize file trees   Author: Kees J. Bot
00002  *                                                              10 Jun 1994
00003  */
00004 #define nil 0
00005 #include <sys/types.h>
00006 #include <sys/stat.h>
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #include <stdarg.h>
00010 #include <string.h>
00011 #include <dirent.h>
00012 #include <unistd.h>
00013 #include <fcntl.h>
00014 #include <errno.h>
00015 #include <limits.h>
00016 #include <time.h>
00017 #include <utime.h>
00018 
00019 #define arraysize(a)    (sizeof(a) / sizeof((a)[0]))
00020 #define arraylimit(a)   ((a) + arraysize(a))
00021 
00022 #ifndef major
00023 #define major(dev)      ((int) ((dev) >> 8))
00024 #define minor(dev)      ((int) ((dev) & 0xFF))
00025 #endif
00026 
00027 #ifndef S_ISLNK
00028 /* There were no symlinks in medieval times. */
00029 #define S_ISLNK(mode)                   (0)
00030 #define lstat                           stat
00031 #define symlink(path1, path2)           (errno= ENOSYS, -1)
00032 #define readlink(path, buf, len)        (errno= ENOSYS, -1)
00033 #endif
00034 
00035 int sflag;              /* Make state file. */
00036 int dflag;              /* Make list of differences. */
00037 int uflag;              /* Only update files with newer versions. */
00038 int xflag;              /* Do not cross device boundaries. */
00039 int Dflag;              /* Debug: Readable differences, no file contents. */
00040 int vflag;              /* Verbose. */
00041 
00042 #define NO_DEVICE       (-1)
00043 dev_t xdev= NO_DEVICE;  /* The device that you should stay within. */
00044 
00045 int excode= 0;          /* Exit(excode); */
00046 
00047 #define BASE_INDENT     2       /* State file basic indent. */
00048 
00049 void report(const char *label)
00050 {
00051         fprintf(stderr, "remsync: %s: %s\n", label, strerror(errno));
00052         excode= 1;
00053 }
00054 
00055 void fatal(const char *label)
00056 {
00057         report(label);
00058         exit(1);
00059 }
00060 
00061 void *allocate(void *mem, size_t size)
00062 {
00063         if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil) {
00064                 fprintf(stderr, "remsync: Out of memory: %s\n",
00065                         strerror(errno));
00066                 exit(1);
00067         }
00068         return mem;
00069 }
00070 
00071 void deallocate(void *mem)
00072 {
00073         if (mem != nil) free(mem);
00074 }
00075 
00076 /* One needs to slowly forget two sets of objects: for the code that reads
00077  * the state file, and for the code that traverses trees.
00078  */
00079 int keep;
00080 #define KEEP_STATE      0
00081 #define KEEP_TRAVERSE   1
00082 
00083 void forget(void *mem)
00084 /* Some objects must be deleted in time, but not just yet. */
00085 {
00086         static void *death_row[2][50];
00087         static void **dp[2]= { death_row[0], death_row[1] };
00088 
00089         deallocate(*dp[keep]);
00090         *dp[keep]++= mem;
00091         if (dp[keep] == arraylimit(death_row[keep])) dp[keep]= death_row[keep];
00092 }
00093 
00094 char *copystr(const char *s)
00095 {
00096         char *c= allocate(nil, (strlen(s) + 1) * sizeof(c[0]));
00097         strcpy(c, s);
00098         return c;
00099 }
00100 
00101 typedef struct pathname {
00102         char            *path;  /* The actual pathname. */
00103         size_t          idx;    /* Index for the terminating null byte. */
00104         size_t          lim;    /* Actual length of the path array. */
00105 } pathname_t;
00106 
00107 void path_init(pathname_t *pp)
00108 /* Initialize a pathname to the null string. */
00109 {
00110         pp->path= allocate(nil, (pp->lim= 16) * sizeof(pp->path[0]));
00111         pp->path[pp->idx= 0]= 0;
00112 }
00113 
00114 void path_add(pathname_t *pp, const char *name)
00115 /* Add a component to a pathname. */
00116 {
00117         size_t lim;
00118         char *p;
00119         int slash;
00120 
00121         lim= pp->idx + strlen(name) + 2;
00122 
00123         if (lim > pp->lim) {
00124                 pp->lim= lim + lim/2;   /* add an extra 50% growing space. */
00125                 pp->path= allocate(pp->path, pp->lim * sizeof(pp->path[0]));
00126         }
00127 
00128         p= pp->path + pp->idx;
00129         slash= (pp->idx > 0);
00130         if (pp->idx == 1 && p[-1] == '/') p--;
00131 
00132         while (*name != 0) {
00133                 if (*name == '/') {
00134                         slash= 1;
00135                 } else {
00136                         if (slash) { *p++ = '/'; slash= 0; }
00137                         *p++= *name;
00138                 }
00139                 name++;
00140         }
00141         if (slash && p == pp->path) *p++= '/';
00142         *p = 0;
00143         pp->idx= p - pp->path;
00144 }
00145 
00146 void path_trunc(pathname_t *pp, size_t didx)
00147 /* Delete part of a pathname to a remembered length. */
00148 {
00149         pp->path[pp->idx= didx]= 0;
00150 }
00151 
00152 #if kept_for_comments_only
00153 
00154 const char *path_name(const pathname_t *pp)
00155 /* Return the actual name as a char array. */
00156 {
00157         return pp->path;
00158 }
00159 
00160 size_t path_length(const pathname_t *pp)
00161 /* The length of the pathname. */
00162 {
00163         return pp->idx;
00164 }
00165 
00166 void path_drop(pathname_t *pp)
00167 /* Release the storage occupied by the pathname. */
00168 {
00169         free(pp->path);
00170 }
00171 #endif
00172 
00173 #define path_name(pp)           ((const char *) (pp)->path)
00174 #define path_length(pp)         ((pp)->idx)
00175 #define path_drop(pp)           free((void *) (pp)->path)
00176 
00177 typedef struct namelist {       /* Obviously a list of names. */
00178         struct namelist *next;
00179         char            *name;
00180 } namelist_t;
00181 
00182 char *rdlink(const char *link, off_t size)
00183 /* Look where "link" points. */
00184 {
00185         static char *path= nil;
00186         static size_t len= 0;
00187         size_t n;
00188 
00189         if (len <= size) {
00190                 path= allocate(path, (len= size * 2) * sizeof(path[0]));
00191         }
00192         if ((n= readlink(link, path, len)) == -1) return nil;
00193         path[n]= 0;
00194         return path;
00195 }
00196 
00197 void sort(namelist_t **anl)
00198 /* A stable mergesort disguised as line noise.  Must be called like this:
00199  *      if (L!=nil && L->next!=nil) sort(&L);
00200  */
00201 {
00202         /* static */ namelist_t *nl1, **mid;  /* Need not be local */
00203         namelist_t *nl2;
00204 
00205         nl1= *(mid= &(*anl)->next);
00206         do {
00207                 if ((nl1= nl1->next) == nil) break;
00208                 mid= &(*mid)->next;
00209         } while ((nl1= nl1->next) != nil);
00210 
00211         nl2= *mid;
00212         *mid= nil;
00213 
00214         if ((*anl)->next != nil) sort(anl);
00215         if (nl2->next != nil) sort(&nl2);
00216 
00217         nl1= *anl;
00218         for (;;) {
00219                 if (strcmp(nl1->name, nl2->name)<=0) {
00220                         if ((nl1= *(anl= &nl1->next)) == nil) {
00221                                 *anl= nl2;
00222                                 break;
00223                         }
00224                 } else {
00225                         *anl= nl2;
00226                         nl2= *(anl= &nl2->next);
00227                         *anl= nl1;
00228                         if (nl2 == nil) break;
00229                 }
00230         }
00231 }
00232 
00233 namelist_t *collect(const char *dir)
00234 /* Return a sorted list of directory entries.  Returns null with errno != 0
00235  * on error.
00236  */
00237 {
00238         namelist_t *names, **pn= &names;
00239         DIR *dp;
00240         struct dirent *entry;
00241 
00242         if ((dp= opendir(dir)) == nil) return nil;
00243 
00244         while ((entry= readdir(dp)) != nil) {
00245                 if (entry->d_name[0] == '.'
00246                         && (entry->d_name[1] == 0
00247                                 || (entry->d_name[1] == '.'
00248                                         && entry->d_name[2] == 0))) {
00249                         continue;
00250                 }
00251                 *pn= allocate(nil, sizeof(**pn));
00252                 (*pn)->name= copystr(entry->d_name);
00253                 pn= &(*pn)->next;
00254         }
00255         closedir(dp);
00256         *pn= nil;
00257         errno= 0;
00258         if (names != nil && names->next != nil) sort(&names);
00259         return names;
00260 }
00261 
00262 char *pop_name(namelist_t **names)
00263 /* Return one name of a name list. */
00264 {
00265         char *name;
00266         namelist_t *junk;
00267 
00268         junk= *names;
00269         *names= junk->next;
00270         name= junk->name;
00271         deallocate(junk);
00272         forget(name);
00273         return name;
00274 }
00275 
00276 typedef enum filetype {         /* The files we know about. */
00277         F_DIR,
00278         F_FILE,
00279         F_BLK,
00280         F_CHR,
00281         F_PIPE,
00282         F_LINK
00283 } filetype_t;
00284 
00285 typedef struct entry {          /* One file. */
00286         int             depth;          /* Depth in directory tree. */
00287         const char      *name;          /* Name of entry. */
00288         const char      *path;          /* Path name. */
00289         int             ignore;         /* Ignore this entry (errno number.) */
00290         unsigned long   fake_ino;       /* Fake inode number for hard links. */
00291         int             linked;         /* Is the file hard linked? */
00292         int             lastlink;       /* Is it the last link? */
00293         char            *link;          /* Where a (sym)link points to. */
00294         filetype_t      type;
00295         mode_t          mode;           /* Not unlike those in struct stat. */
00296         uid_t           uid;
00297         gid_t           gid;
00298         off_t           size;
00299         time_t          mtime;
00300         dev_t           rdev;
00301 } entry_t;
00302 
00303 void linked(entry_t *entry, struct stat *stp)
00304 /* Return an "inode number" if a file could have links (link count > 1).
00305  * Also return a path to the first link if you see the file again.
00306  */
00307 {
00308         static unsigned long new_fake_ino= 0;
00309         static struct links {
00310                 struct links    *next;
00311                 char            *path;
00312                 ino_t           ino;
00313                 dev_t           dev;
00314                 nlink_t         nlink;
00315                 unsigned long   fake_ino;
00316         } *links[1024];
00317         struct links **plp, *lp;
00318 
00319         entry->linked= entry->lastlink= 0;
00320         entry->fake_ino= 0;
00321         entry->link= nil;
00322 
00323         if (S_ISDIR(stp->st_mode) || stp->st_nlink < 2) return;
00324 
00325         plp= &links[stp->st_ino % arraysize(links)];
00326         while ((lp= *plp) != nil && (lp->ino != stp->st_ino
00327                                 || lp->dev != stp->st_dev)) plp= &lp->next;
00328 
00329         if (lp == nil) {
00330                 /* New file, store it with a new fake inode number. */
00331                 *plp= lp= allocate(nil, sizeof(*lp));
00332                 lp->next= nil;
00333                 lp->path= copystr(entry->path);
00334                 lp->ino= stp->st_ino;
00335                 lp->dev= stp->st_dev;
00336                 lp->nlink= stp->st_nlink;
00337                 lp->fake_ino= ++new_fake_ino;
00338         } else {
00339                 entry->link= lp->path;
00340                 entry->linked= 1;
00341         }
00342         entry->fake_ino= lp->fake_ino;
00343 
00344         if (--lp->nlink == 0) {
00345                 /* No need to remember this one, no more links coming. */
00346                 *plp= lp->next;
00347                 forget(lp->path);
00348                 deallocate(lp);
00349                 entry->lastlink= 1;
00350         }
00351 }
00352 
00353 char *tree;             /* Tree to work on. */
00354 FILE *statefp;          /* State file. */
00355 char *state_file;
00356 FILE *difffp;           /* File of differences. */
00357 char *diff_file;
00358 
00359 entry_t *traverse(void)
00360 /* Get one name from the directory tree. */
00361 {
00362         static int depth;
00363         static pathname_t path;
00364         static entry_t entry;
00365         static namelist_t **entries;
00366         static size_t *trunc;
00367         static size_t deep;
00368         static namelist_t *newentries;
00369         struct stat st;
00370 
00371 recurse:
00372         keep= KEEP_TRAVERSE;
00373 
00374         if (deep == 0) {
00375                 /* Initialize for the root of the tree. */
00376                 path_init(&path);
00377                 path_add(&path, tree);
00378                 entries= allocate(nil, 1 * sizeof(entries[0]));
00379                 entries[0]= allocate(nil, sizeof(*entries[0]));
00380                 entries[0]->next= nil;
00381                 entries[0]->name= copystr("/");
00382                 trunc= allocate(nil, 1 * sizeof(trunc[0]));
00383                 trunc[0]= path_length(&path);
00384                 deep= 1;
00385         } else
00386         if (newentries != nil) {
00387                 /* Last entry was a directory, need to go down. */
00388                 if (entry.ignore) {
00389                         /* Ouch, it is to be ignored! */
00390                         while (newentries != nil) (void) pop_name(&newentries);
00391                         goto recurse;
00392                 }
00393                 if (++depth == deep) {
00394                         deep++;
00395                         entries= allocate(entries, deep * sizeof(entries[0]));
00396                         trunc= allocate(trunc, deep * sizeof(trunc[0]));
00397                 }
00398                 entries[depth]= newentries;
00399                 newentries= nil;
00400                 trunc[depth]= path_length(&path);
00401         } else {
00402                 /* Pop up out of emptied directories. */
00403                 while (entries[depth] == nil) {
00404                         if (depth == 0) return nil;     /* Back at the root. */
00405 
00406                         /* Go up one level. */
00407                         depth--;
00408                 }
00409         }
00410         entry.name= pop_name(&entries[depth]);
00411         path_trunc(&path, trunc[depth]);
00412         path_add(&path, entry.name);
00413         if (depth == 0) {
00414                 entry.path= "/";
00415         } else {
00416                 entry.path= path_name(&path) + trunc[0];
00417                 if (entry.path[0] == '/') entry.path++;
00418         }
00419         entry.depth= depth;
00420         entry.ignore= 0;
00421 
00422         if (lstat(path_name(&path), &st) < 0) {
00423                 if (depth == 0 || errno != ENOENT) {
00424                         /* Something wrong with this entry, complain about
00425                          * it and ignore it further.
00426                          */
00427                         entry.ignore= errno;
00428                         report(path_name(&path));
00429                         return &entry;
00430                 } else {
00431                         /* Entry strangely nonexistent; simply continue. */
00432                         goto recurse;
00433                 }
00434         }
00435 
00436         /* Don't cross mountpoints if -x is set. */
00437         if (xflag) {
00438                 if (xdev == NO_DEVICE) xdev= st.st_dev;
00439                 if (st.st_dev != xdev) {
00440                         /* Ignore the mountpoint. */
00441                         entry.ignore= EXDEV;
00442                         return &entry;
00443                 }
00444         }
00445 
00446         entry.mode= st.st_mode & 07777;
00447         entry.uid= st.st_uid;
00448         entry.gid= st.st_gid;
00449         entry.size= st.st_size;
00450         entry.mtime= st.st_mtime;
00451         entry.rdev= st.st_rdev;
00452 
00453         linked(&entry, &st);
00454 
00455         if (S_ISDIR(st.st_mode)) {
00456                 /* A directory. */
00457                 entry.type= F_DIR;
00458 
00459                 /* Gather directory entries for the next traverse. */
00460                 if ((newentries= collect(path_name(&path))) == nil
00461                                                         && errno != 0) {
00462                         entry.ignore= errno;
00463                         report(path_name(&path));
00464                 }
00465         } else
00466         if (S_ISREG(st.st_mode)) {
00467                 /* A plain file. */
00468                 entry.type= F_FILE;
00469         } else
00470         if (S_ISBLK(st.st_mode)) {
00471                 /* A block special file. */
00472                 entry.type= F_BLK;
00473         } else
00474         if (S_ISCHR(st.st_mode)) {
00475                 /* A character special file. */
00476                 entry.type= F_CHR;
00477         } else
00478         if (S_ISFIFO(st.st_mode)) {
00479                 /* A named pipe. */
00480                 entry.type= F_PIPE;
00481         } else
00482         if (S_ISLNK(st.st_mode)) {
00483                 /* A symbolic link. */
00484                 entry.type= F_LINK;
00485                 if ((entry.link= rdlink(path_name(&path), st.st_size)) == nil) {
00486                         entry.ignore= errno;
00487                         report(path_name(&path));
00488                 }
00489         } else {
00490                 /* Unknown type of file. */
00491                 entry.ignore= EINVAL;
00492         }
00493         return &entry;
00494 }
00495 
00496 void checkstate(void)
00497 {
00498         if (ferror(statefp)) fatal(state_file);
00499 }
00500 
00501 void indent(int depth)
00502 /* Provide indentation to show directory depth. */
00503 {
00504         int n= BASE_INDENT * (depth - 1);
00505 
00506         while (n >= 8) {
00507                 if (putc('\t', statefp) == EOF) checkstate();
00508                 n-= 8;
00509         }
00510         while (n > 0) {
00511                 if (putc(' ', statefp) == EOF) checkstate();
00512                 n--;
00513         }
00514 }
00515 
00516 int print_name(FILE *fp, const char *name)
00517 /* Encode a name. */
00518 {
00519         const char *p;
00520         int c;
00521 
00522         for (p= name; (c= (unsigned char) *p) != 0; p++) {
00523                 if (c <= ' ' || c == '\\') {
00524                         fprintf(fp, "\\%03o", c);
00525                         if (ferror(fp)) return 0;
00526                 } else {
00527                         if (putc(c, fp) == EOF) return 0;
00528                 }
00529         }
00530         return 1;
00531 }
00532 
00533 void mkstatefile(void)
00534 /* Make a state file out of the directory tree. */
00535 {
00536         entry_t *entry;
00537 
00538         while ((entry= traverse()) != nil) {
00539                 indent(entry->depth);
00540                 if (!print_name(statefp, entry->name)) checkstate();
00541 
00542                 if (entry->ignore) {
00543                         fprintf(statefp, "\tignore (%s)\n",
00544                                 strerror(entry->ignore));
00545                         checkstate();
00546                         continue;
00547                 }
00548 
00549                 switch (entry->type) {
00550                 case F_DIR:
00551                         fprintf(statefp, "\td%03o %u %u",
00552                                 (unsigned) entry->mode,
00553                                 (unsigned) entry->uid, (unsigned) entry->gid);
00554                         break;
00555                 case F_FILE:
00556                         fprintf(statefp, "\t%03o %u %u %lu %lu",
00557                                 (unsigned) entry->mode,
00558                                 (unsigned) entry->uid, (unsigned) entry->gid,
00559                                 (unsigned long) entry->size,
00560                                 (unsigned long) entry->mtime);
00561                         break;
00562                 case F_BLK:
00563                 case F_CHR:
00564                         fprintf(statefp, "\t%c%03o %u %u %x",
00565                                 entry->type == F_BLK ? 'b' : 'c',
00566                                 (unsigned) entry->mode,
00567                                 (unsigned) entry->uid, (unsigned) entry->gid,
00568                                 (unsigned) entry->rdev);
00569                         break;
00570                 case F_PIPE:
00571                         fprintf(statefp, "\tp%03o %u %u",
00572                                 (unsigned) entry->mode,
00573                                 (unsigned) entry->uid, (unsigned) entry->gid);
00574                         break;
00575                 case F_LINK:
00576                         fprintf(statefp, "\t-> ");
00577                         checkstate();
00578                         (void) print_name(statefp, entry->link);
00579                         break;
00580                 }
00581                 checkstate();
00582                 if (entry->fake_ino != 0)
00583                         fprintf(statefp, " %lu", entry->fake_ino);
00584                 if (entry->lastlink)
00585                         fprintf(statefp, " last");
00586                 if (fputc('\n', statefp) == EOF) checkstate();
00587         }
00588         fflush(statefp);
00589         checkstate();
00590 }
00591 
00592 char *read1line(FILE *fp)
00593 /* Read one line from a file.  Return null on EOF or error. */
00594 {
00595         static char *line;
00596         static size_t len;
00597         size_t idx;
00598         int c;
00599 
00600         if (len == 0) line= allocate(nil, (len= 16) * sizeof(line[0]));
00601 
00602         idx= 0;
00603         while ((c= getc(fp)) != EOF && c != '\n') {
00604                 if (c < '\t') {
00605                         /* Control characters are not possible. */
00606                         fprintf(stderr,
00607                                 "remsync: control character in data file!\n");
00608                         exit(1);
00609                 }
00610                 line[idx++]= c;
00611                 if (idx == len) {
00612                         line= allocate(line, (len*= 2) * sizeof(line[0]));
00613                 }
00614         }
00615         if (c == EOF) {
00616                 if (ferror(fp)) return nil;
00617                 if (idx == 0) return nil;
00618         }
00619         line[idx]= 0;
00620         return line;
00621 }
00622 
00623 void getword(char **pline, char **parg, size_t *plen)
00624 /* Get one word from a line, interpret octal escapes. */
00625 {
00626         char *line= *pline;
00627         char *arg= *parg;
00628         size_t len= *plen;
00629         int i;
00630         int c;
00631         size_t idx;
00632 
00633         idx= 0;
00634         while ((c= *line) != 0 && c != ' ' && c != '\t') {
00635                 line++;
00636                 if (c == '\\') {
00637                         c= 0;
00638                         for (i= 0; i < 3; i++) {
00639                                 if ((unsigned) (*line - '0') >= 010) break;
00640                                 c= (c << 3) | (*line - '0');
00641                                 line++;
00642                         }
00643                 }
00644                 arg[idx++]= c;
00645                 if (idx == len) arg= allocate(arg, (len*= 2) * sizeof(arg[0]));
00646         }
00647         arg[idx]= 0;
00648         *pline= line;
00649         *parg= arg;
00650         *plen= len;
00651 }
00652 
00653 void splitline(char *line, char ***pargv, size_t *pargc)
00654 /* Split a line into an array of words. */
00655 {
00656         static char **argv;
00657         static size_t *lenv;
00658         static size_t len;
00659         size_t idx;
00660 
00661         idx= 0;
00662         for (;;) {
00663                 while (*line == ' ' || *line == '\t') line++;
00664 
00665                 if (*line == 0) break;
00666 
00667                 if (idx == len) {
00668                         len++;
00669                         argv= allocate(argv, len * sizeof(argv[0]));
00670                         lenv= allocate(lenv, len * sizeof(lenv[0]));
00671                         argv[idx]= allocate(nil, 16 * sizeof(argv[idx][0]));
00672                         lenv[idx]= 16;
00673                 }
00674                 getword(&line, &argv[idx], &lenv[idx]);
00675                 idx++;
00676         }
00677         *pargv= argv;
00678         *pargc= idx;
00679 }
00680 
00681 int getattributes(entry_t *entry, int argc, char **argv)
00682 /* Convert state or difference file info into file attributes. */
00683 {
00684         int i;
00685         int attr;
00686 #define A_MODE1         0x01    /* Some of these attributes follow the name */
00687 #define A_MODE          0x02
00688 #define A_OWNER         0x04
00689 #define A_SIZETIME      0x08
00690 #define A_DEV           0x10
00691 #define A_LINK          0x20
00692 
00693         switch (argv[0][0]) {
00694         case 'd':
00695                 /* Directory. */
00696                 entry->type= F_DIR;
00697                 attr= A_MODE1 | A_OWNER;
00698                 break;
00699         case 'b':
00700                 /* Block device. */
00701                 entry->type= F_BLK;
00702                 attr= A_MODE1 | A_OWNER | A_DEV;
00703                 break;
00704         case 'c':
00705                 /* Character device. */
00706                 entry->type= F_CHR;
00707                 attr= A_MODE1 | A_OWNER | A_DEV;
00708                 break;
00709         case 'p':
00710                 /* Named pipe. */
00711                 entry->type= F_PIPE;
00712                 attr= A_MODE1 | A_OWNER;
00713                 break;
00714         case '-':
00715                 /* Symlink. */
00716                 entry->type= F_LINK;
00717                 attr= A_LINK;
00718                 break;
00719         default:
00720                 /* Normal file. */
00721                 entry->type= F_FILE;
00722                 attr= A_MODE | A_OWNER | A_SIZETIME;
00723         }
00724 
00725         if (attr & (A_MODE | A_MODE1)) {
00726                 entry->mode= strtoul(argv[0] + (attr & A_MODE1), nil, 010);
00727         }
00728         i= 1;
00729         if (attr & A_OWNER) {
00730                 if (i + 2 > argc) return 0;
00731                 entry->uid= strtoul(argv[i++], nil, 10);
00732                 entry->gid= strtoul(argv[i++], nil, 10);
00733         }
00734         if (attr & A_SIZETIME) {
00735                 if (i + 2 > argc) return 0;
00736                 entry->size= strtoul(argv[i++], nil, 10);
00737                 entry->mtime= strtoul(argv[i++], nil, 10);
00738         }
00739         if (attr & A_DEV) {
00740                 if (i + 1 > argc) return 0;
00741                 entry->rdev= strtoul(argv[i++], nil, 0x10);
00742         }
00743         if (attr & A_LINK) {
00744                 if (i + 1 > argc) return 0;
00745                 entry->link= argv[i++];
00746         }
00747         entry->linked= entry->lastlink= 0;
00748         if (i < argc) {
00749                 /* It has a fake inode number, so it is a hard link. */
00750                 static struct links {   /* List of hard links. */
00751                         struct links    *next;
00752                         unsigned long   fake_ino;
00753                         char            *path;
00754                 } *links[1024];
00755                 struct links **plp, *lp;
00756                 unsigned long fake_ino;
00757 
00758                 fake_ino= strtoul(argv[i++], nil, 10);
00759 
00760                 plp= &links[fake_ino % arraysize(links)];
00761                 while ((lp= *plp) != nil && lp->fake_ino != fake_ino)
00762                         plp= &lp->next;
00763 
00764                 if (lp == nil) {
00765                         /* New link. */
00766                         *plp= lp= allocate(nil, sizeof(*lp));
00767                         lp->next= nil;
00768                         lp->fake_ino= fake_ino;
00769                         lp->path= copystr(entry->path);
00770                 } else {
00771                         /* Linked to. */
00772                         entry->link= lp->path;
00773                         entry->linked= 1;
00774                 }
00775 
00776                 if (i < argc) {
00777                         if (strcmp(argv[i++], "last") != 0) return 0;
00778 
00779                         /* Last hard link of a file. */
00780                         forget(lp->path);
00781                         *plp= lp->next;
00782                         deallocate(lp);
00783                         entry->lastlink= 1;
00784                 }
00785         }
00786         if (i != argc) return 0;
00787         return 1;
00788 }
00789 
00790 void state_syntax(off_t line)
00791 {
00792         fprintf(stderr, "remsync: %s: syntax error on line %lu\n",
00793                 state_file, (unsigned long) line);
00794         exit(1);
00795 }
00796 
00797 entry_t *readstate(void)
00798 /* Read one entry from the state file. */
00799 {
00800         static entry_t entry;
00801         static pathname_t path;
00802         static size_t *trunc;
00803         static size_t trunc_len;
00804         static base_indent;
00805         char *line;
00806         char **argv;
00807         size_t argc;
00808         static off_t lineno;
00809         int indent, depth;
00810 
00811 recurse:
00812         keep= KEEP_STATE;
00813 
00814         if (feof(statefp) || (line= read1line(statefp)) == nil) {
00815                 checkstate();
00816                 return nil;
00817         }
00818         lineno++;
00819 
00820         /* How far is this entry indented? */
00821         indent= 0;
00822         while (*line != 0) {
00823                 if (*line == ' ') indent++;
00824                 else
00825                 if (*line == '\t') indent= (indent + 8) & ~7;
00826                 else
00827                         break;
00828                 line++;
00829         }
00830         if (indent > 0 && base_indent == 0) base_indent= indent;
00831         depth= (base_indent == 0 ? 0 : indent / base_indent) + 1;
00832 
00833         if (entry.ignore && depth > entry.depth) {
00834                 /* If the old directory is ignored, then so are its entries. */
00835                 goto recurse;
00836         }
00837         entry.depth= depth;
00838 
00839         splitline(line, &argv, &argc);
00840         if (argc < 2) state_syntax(lineno);
00841 
00842         if (trunc == nil) {
00843                 /* The root of the tree, initialize path. */
00844                 if (argv[0][0] != '/') state_syntax(lineno);
00845                 path_init(&path);
00846                 path_add(&path, "/");
00847                 trunc= allocate(nil, (trunc_len= 16) * sizeof(trunc[0]));
00848 
00849                 /* The root has depth 0. */
00850                 entry.depth= 0;
00851                 trunc[0]= 0;
00852         } else {
00853                 if (entry.depth > trunc_len) {
00854                         trunc= allocate(trunc,
00855                                         (trunc_len*= 2) * sizeof(trunc[0]));
00856                 }
00857                 path_trunc(&path, trunc[entry.depth - 1]);
00858                 path_add(&path, argv[0]);
00859                 trunc[entry.depth]= path_length(&path);
00860         }
00861 
00862         entry.path= path_name(&path);
00863         entry.name= argv[0];
00864         entry.link= nil;
00865         if ((entry.ignore= strcmp(argv[1], "ignore") == 0)) {
00866                 return &entry;
00867         }
00868         if (!getattributes(&entry, argc - 1, argv + 1)) state_syntax(lineno);
00869         return &entry;
00870 }
00871 
00872 void checkdiff(void)
00873 {
00874         if (ferror(difffp)) fatal(diff_file);
00875 }
00876 
00877 enum { DELETE, REPLACE, COPY, SIMILAR, EQUAL, ADD }
00878 compare(entry_t *remote, entry_t *local)
00879 /* Compare the local and remote entries and tell what need to be done. */
00880 {
00881         int cmp;
00882 
00883         /* Surplus entries? */
00884         if (local == nil) return DELETE;
00885         if (remote == nil) return ADD;
00886 
00887         /* Extra directory entries? */
00888         if (remote->depth > local->depth) return DELETE;
00889         if (local->depth > remote->depth) return ADD;
00890 
00891         /* Compare names. */
00892         cmp= strcmp(remote->name, local->name);
00893         if (cmp < 0) return DELETE;
00894         if (cmp > 0) return ADD;
00895 
00896         /* The files have the same name.  Ignore one, ignore the other. */
00897         if (remote->ignore || local->ignore) {
00898                 remote->ignore= local->ignore= 1;
00899                 return EQUAL;
00900         }
00901 
00902         /* Reasons for replacement? */
00903         if (remote->type != local->type) return REPLACE;
00904 
00905         /* Should be hard linked to the same file. */
00906         if (remote->linked || local->linked) {
00907                 if (!remote->linked || !local->linked) return REPLACE;
00908                 if (strcmp(remote->link, local->link) != 0) return REPLACE;
00909         }
00910 
00911         switch (remote->type) {
00912         case F_FILE:
00913                 if (uflag) {
00914                         if (remote->mtime < local->mtime) return COPY;
00915                 } else {
00916                         if (remote->size != local->size
00917                                         || remote->mtime != local->mtime)
00918                                 return COPY;
00919                 }
00920                 goto check_modes;
00921         case F_BLK:
00922         case F_CHR:
00923                 if (remote->rdev != local->rdev) return REPLACE;
00924                 goto check_modes;
00925         case F_DIR:
00926         case F_PIPE:
00927         check_modes:
00928                 if (remote->mode != local->mode
00929                         || remote->uid != local->uid
00930                         || remote->gid != local->gid) return SIMILAR;
00931                 break;
00932         case F_LINK:
00933                 if (strcmp(remote->link, local->link) != 0) return REPLACE;
00934                 break;
00935         }
00936         return EQUAL;
00937 }
00938 
00939 void delete(entry_t *old)
00940 /* Emit an instruction to remove an entry. */
00941 {
00942         if (old->ignore) return;
00943         if (uflag) return;
00944 
00945         fprintf(difffp, "rm ");
00946         checkdiff();
00947         if (!print_name(difffp, old->path)) checkdiff();
00948         if (putc('\n', difffp) == EOF) checkdiff();
00949         if (vflag) fprintf(stderr, "rm %s\n", old->path);
00950 }
00951 
00952 void change_modes(entry_t *old, entry_t *new)
00953 /* Emit an instruction to change the attributes of an entry. */
00954 {
00955         if (new->ignore) return;
00956 
00957         fprintf(difffp, "chmod ");
00958         checkdiff();
00959         if (!print_name(difffp, new->path)) checkdiff();
00960         fprintf(difffp, " %03o %u %u\n",
00961                 (unsigned) new->mode,
00962                 (unsigned) new->uid, (unsigned) new->gid);
00963         checkdiff();
00964         if (vflag && old->mode != new->mode) {
00965                 fprintf(stderr, "chmod %s %03o %u %u\n",
00966                         new->path,
00967                         (unsigned) new->mode,
00968                         (unsigned) new->uid, (unsigned) new->gid);
00969         }
00970 }
00971 
00972 int cat(int f, off_t size)
00973 /* Include the contents of a file in the differences file. */
00974 {
00975         ssize_t n;
00976         unsigned char buf[1024 << sizeof(int)];
00977         unsigned char *p;
00978         int c;
00979 
00980         if (Dflag) return 1;    /* Debug: Don't need the file contents. */
00981 
00982         while ((n= read(f, buf, sizeof(buf))) > 0) {
00983                 p= buf;
00984                 do {
00985                         if (size == 0) {
00986                                 /* File suddenly larger. */
00987                                 errno= EINVAL;
00988                                 return 0;
00989                         }
00990                         c= *p++;
00991                         if (putc(c, difffp) == EOF) checkdiff();
00992                         size--;
00993                 } while (--n != 0);
00994         }
00995         if (size > 0) {
00996                 int err= errno;
00997 
00998                 /* File somehow shrunk, pad it out. */
00999                 do {
01000                         if (putc(0, difffp) == EOF) checkdiff();
01001                 } while (--size != 0);
01002                 errno= n == 0 ? EINVAL : err;
01003                 n= -1;
01004         }
01005         return n == 0;
01006 }
01007 
01008 void add(entry_t *old, entry_t *new)
01009 /* Emit an instruction to add an entry. */
01010 {
01011         pathname_t file;
01012         int f;
01013 
01014         if (new->ignore) return;
01015 
01016         if (new->linked) {
01017                 /* This file is to be a hard link to an existing file. */
01018                 fprintf(difffp, "ln ");
01019                 checkdiff();
01020                 if (!print_name(difffp, new->link)) checkdiff();
01021                 if (fputc(' ', difffp) == EOF) checkdiff();
01022                 if (!print_name(difffp, new->path)) checkdiff();
01023                 if (fputc('\n', difffp) == EOF) checkdiff();
01024                 if (vflag) {
01025                         fprintf(stderr, "ln %s %s\n", new->link, new->path);
01026                 }
01027                 return;
01028         }
01029 
01030         /* Add some other type of file. */
01031         fprintf(difffp, "add ");
01032         checkdiff();
01033         if (!print_name(difffp, new->path)) checkdiff();
01034 
01035         switch (new->type) {
01036         case F_DIR:
01037                 fprintf(difffp, " d%03o %u %u\n",
01038                         (unsigned) new->mode,
01039                         (unsigned) new->uid, (unsigned) new->gid);
01040                 if (vflag) fprintf(stderr, "mkdir %s\n", new->path);
01041                 break;
01042         case F_FILE:
01043                 path_init(&file);
01044                 path_add(&file, tree);
01045                 path_add(&file, new->path);
01046                 if ((f= open(path_name(&file), O_RDONLY)) < 0) {
01047                         report(path_name(&file));
01048                         path_drop(&file);
01049                         fprintf(difffp, " ignore\n");
01050                         break;
01051                 }
01052                 fprintf(difffp, " %03o %u %u %lu %lu\n",
01053                         (unsigned) new->mode,
01054                         (unsigned) new->uid, (unsigned) new->gid,
01055                         (unsigned long) new->size,
01056                         (unsigned long) new->mtime);
01057                 checkdiff();
01058                 if (!cat(f, new->size)) {
01059                         int err= errno;
01060                         report(path_name(&file));
01061                         fprintf(difffp, "old ");
01062                         checkdiff();
01063                         print_name(difffp, err == EINVAL
01064                                 ? "File changed when copied" : strerror(err));
01065                         fputc('\n', difffp);
01066                         checkdiff();
01067                 } else {
01068                         if (vflag) {
01069                                 fprintf(stderr, "%s %s\n",
01070                                         old == nil ? "add" :
01071                                                 old->mtime > new->mtime ?
01072                                                         "restore" : "update",
01073                                         new->path);
01074                         }
01075                 }
01076                 close(f);
01077                 path_drop(&file);
01078                 break;
01079         case F_BLK:
01080         case F_CHR:
01081                 fprintf(difffp, " %c%03o %u %u %lx\n",
01082                         new->type == F_BLK ? 'b' : 'c',
01083                         (unsigned) new->mode,
01084                         (unsigned) new->uid, (unsigned) new->gid,
01085                         (unsigned long) new->rdev);
01086                 if (vflag) fprintf(stderr, "mknod %s\n", new->path);
01087                 break;
01088         case F_PIPE:
01089                 fprintf(difffp, " p%03o %u %u\n",
01090                         (unsigned) new->mode,
01091                         (unsigned) new->uid, (unsigned) new->gid);
01092                 if (vflag) fprintf(stderr, "mkfifo %s\n", new->path);
01093                 break;
01094         case F_LINK:
01095                 fprintf(difffp, " -> ");
01096                 checkdiff();
01097                 (void) print_name(difffp, new->link);
01098                 checkdiff();
01099                 fputc('\n', difffp);
01100                 if (vflag) {
01101                         fprintf(stderr, "ln -s %s %s\n", new->link, new->path);
01102                 }
01103                 break;
01104         }
01105         checkdiff();
01106 }
01107 
01108 void mkdifferences(void)
01109 {
01110         entry_t *remote;
01111         entry_t *local;
01112 
01113         remote= readstate();
01114         local= traverse();
01115 
01116         while (remote != nil || local != nil) {
01117                 switch (compare(remote, local)) {
01118                 case DELETE:
01119                         /* Remove the remote file. */
01120                         delete(remote);
01121                         remote->ignore= 1;
01122                         remote= readstate();
01123                         break;
01124                 case REPLACE:
01125                         /* Replace the remote file with the local one. */
01126                         if (remote->type == F_FILE && local->type == F_FILE
01127                                                         && !local->linked) {
01128                                 /* Don't overwrite, remove first. */
01129                                 delete(remote);
01130                         }
01131                         /*FALL THROUGH*/
01132                 case COPY:
01133                         /* Overwrite the remote file with the local one. */
01134                         add(remote, local);
01135                         remote->ignore= 1;
01136                         goto skip2;
01137                 case SIMILAR:
01138                         /* About the same, but the attributes need changing. */
01139                         change_modes(remote, local);
01140                         goto skip2;
01141                 case EQUAL:
01142                 skip2:
01143                         /* Skip two files. */
01144                         remote= readstate();
01145                         local= traverse();
01146                         break;
01147                 case ADD:
01148                         /* Add the local file. */
01149                         add(nil, local);
01150                         local= traverse();
01151                         break;
01152                 }
01153         }
01154         fprintf(difffp, "end\n");
01155         fflush(difffp);
01156         checkdiff();
01157 }
01158 
01159 void apply_remove(pathname_t *pp)
01160 /* Remove an obsolete file. */
01161 {
01162         struct stat st;
01163 
01164         if (lstat(path_name(pp), &st) < 0) {
01165                 if (errno != ENOENT) report(path_name(pp));
01166                 return;
01167         }
01168 
01169         if (S_ISDIR(st.st_mode)) {
01170                 /* Recursively delete directories. */
01171                 size_t len;
01172                 namelist_t *entries;
01173 
01174                 if ((entries= collect(path_name(pp))) == nil && errno != 0) {
01175                         report(path_name(pp));
01176                         return;
01177                 }
01178                 len= path_length(pp);
01179 
01180                 while (entries != nil) {
01181                         path_add(pp, pop_name(&entries));
01182                         apply_remove(pp);
01183                         path_trunc(pp, len);
01184                 }
01185                 if (rmdir(path_name(pp)) < 0) {
01186                         report(path_name(pp));
01187                         return;
01188                 }
01189                 if (vflag) fprintf(stderr, "rmdir %s\n", path_name(pp));
01190         } else {
01191                 /* Some other type of file. */
01192                 if (unlink(path_name(pp)) < 0) {
01193                         report(path_name(pp));
01194                         return;
01195                 }
01196                 if (vflag) fprintf(stderr, "rm %s\n", path_name(pp));
01197         }
01198 }
01199 
01200 void apply_mkold(const char *file, const char *err)
01201 /* Make a file very old.  (An error occurred when it was added.) */
01202 {
01203         struct utimbuf utb;
01204 
01205         utb.actime= utb.modtime= 0;
01206         if (utime(file, &utb) < 0) {
01207                 report(file);
01208                 return;
01209         }
01210         fprintf(stderr, "made %s look old", file);
01211         fprintf(stderr, err == nil ? "\n" : " due to a remote problem: %s\n",
01212                                                                 err);
01213 }
01214 
01215 void apply_chmod(const char *file, mode_t mode, uid_t uid, gid_t gid, int talk)
01216 /* Change mode and ownership. */
01217 {
01218         struct stat st;
01219 
01220         if (lstat(file, &st) < 0) {
01221                 report(file);
01222                 return;
01223         }
01224         if ((st.st_mode & 07777) != mode) {
01225                 if (chmod(file, mode) < 0) {
01226                         report(file);
01227                         return;
01228                 }
01229                 if (vflag && talk) {
01230                         fprintf(stderr, "chmod %03o %s\n",
01231                                                 (unsigned) mode, file);
01232                 }
01233         }
01234         if (st.st_uid != uid || st.st_gid != gid) {
01235                 if (chown(file, uid, gid) < 0) {
01236                         if (errno != EPERM) report(file);
01237                         return;
01238                 }
01239                 if (vflag && talk) {
01240                         fprintf(stderr, "chown %u:%u %s\n",
01241                                 (unsigned) uid, (unsigned) gid, file);
01242                 }
01243         }
01244 }
01245 
01246 void apply_add(pathname_t *pp, entry_t *entry)
01247 /* Add or replace a file. */
01248 {
01249         const char *file;
01250         off_t size;
01251         int f;
01252         unsigned char buf[1024 << sizeof(int)];
01253         unsigned char *p;
01254         int c;
01255         int dirty;
01256         struct stat st;
01257         struct utimbuf utb;
01258 
01259         if (entry->ignore) return;
01260 
01261         if (lstat(path_name(pp), &st) >= 0 && (entry->type != F_FILE
01262                                         || !S_ISREG(st.st_mode))) {
01263                 apply_remove(pp);
01264         }
01265 
01266         file= path_name(pp);
01267 
01268         switch (entry->type) {
01269         case F_DIR:
01270                 if (mkdir(file, entry->mode) < 0) {
01271                         report(file);
01272                         return;
01273                 }
01274                 if (vflag) fprintf(stderr, "mkdir %s\n", file);
01275                 break;
01276         case F_FILE:
01277                 size= entry->size;
01278 
01279                 f= -1;
01280                 st.st_mode= 0;
01281                 if (lstat(file, &st) < 0 || S_ISREG(st.st_mode)) {
01282                         f= open(file, O_WRONLY | O_CREAT | O_TRUNC,
01283                                                 entry->mode);
01284                         if (f < 0) {
01285                                 (void) chmod(file, entry->mode | 0200);
01286                                 f= open(file, O_WRONLY | O_CREAT | O_TRUNC,
01287                                                 entry->mode);
01288                         }
01289                         if (f < 0) {
01290                                 (void) unlink(file);
01291                                 f= open(file, O_WRONLY | O_CREAT | O_TRUNC,
01292                                                 entry->mode);
01293                         }
01294                         if (f < 0) report(file);
01295                 }
01296                 dirty= (f >= 0);
01297                 p= buf;
01298                 while (size > 0 && (c= getc(difffp)) != EOF) {
01299                         size--;
01300                         *p++= c;
01301                         if (p == arraylimit(buf) || size == 0) {
01302                                 if (f >= 0 && write(f, buf, p - buf) < 0) {
01303                                         report(file);
01304                                         close(f);
01305                                         f= -1;
01306                                 }
01307                                 p= buf;
01308                         }
01309                 }
01310                 if (size > 0) {
01311                         if (ferror(difffp)) report(diff_file);
01312                         if (feof(difffp)) {
01313                                 fprintf(stderr, "remspec: %s: premature EOF\n",
01314                                         diff_file);
01315                         }
01316                         if (dirty) apply_mkold(file, nil);
01317                         exit(1);
01318                 }
01319                 if (f < 0) {
01320                         if (dirty) apply_mkold(file, nil);
01321                         return;
01322                 }
01323                 close(f);
01324                 if (vflag) {
01325                         fprintf(stderr, st.st_mode == 0 ? "add %s\n"
01326                                 : entry->mtime >= st.st_mtime
01327                                         ? "update %s\n" : "restore %s\n", file);
01328                 }
01329                 utb.actime= time(nil);
01330                 utb.modtime= entry->mtime;
01331                 if (utime(file, &utb) < 0) report(file);
01332                 break;
01333         case F_BLK:
01334                 if (mknod(file, S_IFBLK | entry->mode, entry->rdev) < 0) {
01335                         report(file);
01336                         return;
01337                 }
01338                 if (vflag) {
01339                         fprintf(stderr, "mknod %s b %d %d\n", file,
01340                                 major(entry->rdev), minor(entry->rdev));
01341                 }
01342                 break;
01343         case F_CHR:
01344                 if (mknod(file, S_IFCHR | entry->mode, entry->rdev) < 0) {
01345                         report(file);
01346                         return;
01347                 }
01348                 if (vflag) {
01349                         fprintf(stderr, "mknod %s c %d %d\n", file,
01350                                 major(entry->rdev), minor(entry->rdev));
01351                 }
01352                 break;
01353         case F_PIPE:
01354                 if (mknod(file, S_IFIFO | entry->mode, 0) < 0) {
01355                         report(file);
01356                         return;
01357                 }
01358                 if (vflag) fprintf(stderr, "mknod %s p\n", file);
01359                 break;
01360         case F_LINK:
01361                 if (symlink(entry->link, file) < 0) {
01362                         report(file);
01363                         return;
01364                 }
01365                 if (vflag) fprintf(stderr, "ln -s %s %s\n", entry->link, file);
01366                 return;
01367         }
01368         apply_chmod(file, entry->mode, entry->uid, entry->gid, 0);
01369 }
01370 
01371 void apply_link(const char *file, pathname_t *pp)
01372 /* Hard link *pp to file. */
01373 {
01374         struct stat st1, st2;
01375 
01376         if (lstat(file, &st1) < 0) {
01377                 report(file);
01378                 return;
01379         }
01380         if (lstat(path_name(pp), &st2) >= 0) {
01381                 if (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)
01382                         return;
01383                 apply_remove(pp);
01384                 if (lstat(path_name(pp), &st2) >= 0) return;
01385         }
01386         if (link(file, path_name(pp)) < 0) {
01387                 fprintf(stderr, "remsync: ln %s %s: %s\n", file, path_name(pp),
01388                         strerror(errno));
01389                 excode= 1;
01390                 return;
01391         }
01392         if (vflag) fprintf(stderr, "ln %s %s\n", file, path_name(pp));
01393 }
01394 
01395 void diff_syntax(const char *line)
01396 {
01397         fprintf(stderr, "remsync: %s: syntax error on this line: %s\n",
01398                 diff_file, line);
01399         exit(1);
01400 }
01401 
01402 void apply_differences(void)
01403 /* Update a tree to a list of differences derived from a remote tree. */
01404 {
01405         char *line;
01406         char **argv;
01407         size_t argc;
01408         pathname_t path, link;
01409         size_t trunc;
01410 
01411         path_init(&path);
01412         path_init(&link);
01413         path_add(&path, tree);
01414         path_add(&link, tree);
01415         trunc= path_length(&path);
01416 
01417         while (!feof(difffp) && (line= read1line(difffp)) != nil) {
01418                 splitline(line, &argv, &argc);
01419                 if (argc == 0) diff_syntax(line);
01420 
01421                 path_trunc(&path, trunc);
01422 
01423                 if (strcmp(argv[0], "add") == 0) {
01424                         entry_t entry;
01425 
01426                         if (argc < 3) diff_syntax(line);
01427                         path_add(&path, argv[1]);
01428                         entry.ignore= (strcmp(argv[2], "ignore") == 0);
01429                         if (!entry.ignore && !getattributes(&entry,
01430                                                         argc - 2, argv + 2))
01431                                 diff_syntax(line);
01432                         apply_add(&path, &entry);
01433                 } else
01434                 if (strcmp(argv[0], "rm") == 0) {
01435                         if (argc != 2) diff_syntax(line);
01436                         path_add(&path, argv[1]);
01437                         apply_remove(&path);
01438                 } else
01439                 if (strcmp(argv[0], "ln") == 0) {
01440                         if (argc != 3) diff_syntax(line);
01441                         path_trunc(&link, trunc);
01442                         path_add(&link, argv[1]);
01443                         path_add(&path, argv[2]);
01444                         apply_link(path_name(&link), &path);
01445                 } else
01446                 if (strcmp(argv[0], "chmod") == 0) {
01447                         if (argc != 5) diff_syntax(line);
01448                         path_add(&path, argv[1]);
01449                         apply_chmod(path_name(&path),
01450                                 strtoul(argv[2], nil, 010),
01451                                 strtoul(argv[3], nil, 10),
01452                                 strtoul(argv[4], nil, 10),
01453                                 1);
01454                 } else
01455                 if (strcmp(argv[0], "old") == 0) {
01456                         if (argc != 3) diff_syntax(line);
01457                         path_add(&path, argv[1]);
01458                         apply_mkold(path_name(&path), argv[2]);
01459                 } else
01460                 if (strcmp(argv[0], "end") == 0) {
01461                         if (argc != 1) diff_syntax(line);
01462                         break;
01463                 } else {
01464                         diff_syntax(line);
01465                 }
01466         }
01467         checkdiff();
01468 }
01469 
01470 void usage(void)
01471 {
01472     fprintf(stderr, "Usage: remsync -sxv tree [state-file]\n");
01473     fprintf(stderr, "       remsync -duxvD tree [state-file [diff-file]]\n");
01474     fprintf(stderr, "       remsync [-xv] tree [diff-file]\n");
01475     exit(1);
01476 }
01477 
01478 int main(int argc, char **argv)
01479 {
01480         int i;
01481 
01482         for (i= 1; i < argc && argv[i][0] == '-'; i++) {
01483                 char *p= argv[i] + 1;
01484 
01485                 if (p[0] == '-' && p[1] == 0) { i++; break; }
01486 
01487                 while (*p != 0) {
01488                         switch (*p++) {
01489                         case 's':       sflag= 1; break;
01490                         case 'd':       dflag= 1; break;
01491                         case 'u':       uflag= 1; break;
01492                         case 'x':       xflag= 1; break;
01493                         case 'D':       Dflag= 1; break;
01494                         case 'v':       vflag= 1; break;
01495                         default:        usage();
01496                         }
01497                 }
01498         }
01499         if (sflag && dflag) usage();
01500         if (sflag && uflag) usage();
01501         if (!sflag && !dflag && uflag) usage();
01502         if (!dflag && Dflag) usage();
01503 
01504         if (i == argc) usage();
01505         tree= argv[i++];
01506 
01507         if (sflag) {
01508                 /* Make a state file. */
01509                 state_file= i < argc ? argv[i++] : "-";
01510                 if (i != argc) usage();
01511 
01512                 statefp= stdout;
01513                 if (strcmp(state_file, "-") != 0) {
01514                         if ((statefp= fopen(state_file, "w")) == nil)
01515                                 fatal(state_file);
01516                 }
01517                 mkstatefile();
01518         } else
01519         if (dflag) {
01520                 /* Make a file of differences. */
01521                 state_file= i < argc ? argv[i++] : "-";
01522 
01523                 diff_file= i < argc ? argv[i++] : "-";
01524                 if (i != argc) usage();
01525 
01526                 statefp= stdin;
01527                 if (strcmp(state_file, "-") != 0) {
01528                         if ((statefp= fopen(state_file, "r")) == nil)
01529                                 fatal(state_file);
01530                 }
01531 
01532                 difffp= stdout;
01533                 if (strcmp(diff_file, "-") != 0) {
01534                         if ((difffp= fopen(diff_file, "w")) == nil)
01535                                 fatal(diff_file);
01536                 }
01537                 mkdifferences();
01538         } else {
01539                 /* Apply a file of differences. */
01540                 diff_file= i < argc ? argv[i++] : "-";
01541                 if (i != argc) usage();
01542 
01543                 difffp= stdin;
01544                 if (strcmp(diff_file, "-") != 0) {
01545                         if ((difffp= fopen(diff_file, "r")) == nil)
01546                                 fatal(diff_file);
01547                 }
01548                 apply_differences();
01549         }
01550         exit(excode);
01551 }

Generated on Fri Apr 14 22:57:10 2006 for minix by  doxygen 1.4.6