📄 remsync.c
字号:
/* remsync 1.5 - remotely synchronize file trees Author: Kees J. Bot * 10 Jun 1994 */#define nil 0#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <string.h>#include <dirent.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <limits.h>#include <time.h>#include <utime.h>#define arraysize(a) (sizeof(a) / sizeof((a)[0]))#define arraylimit(a) ((a) + arraysize(a))#ifndef major#define major(dev) ((int) ((dev) >> 8))#define minor(dev) ((int) ((dev) & 0xFF))#endif#ifndef S_ISLNK/* There were no symlinks in medieval times. */#define S_ISLNK(mode) (0)#define lstat stat#define symlink(path1, path2) (errno= ENOSYS, -1)#define readlink(path, buf, len) (errno= ENOSYS, -1)#endifint sflag; /* Make state file. */int dflag; /* Make list of differences. */int uflag; /* Only update files with newer versions. */int xflag; /* Do not cross device boundaries. */int Dflag; /* Debug: Readable differences, no file contents. */int vflag; /* Verbose. */#define NO_DEVICE (-1)dev_t xdev= NO_DEVICE; /* The device that you should stay within. */int excode= 0; /* Exit(excode); */#define BASE_INDENT 2 /* State file basic indent. */void report(const char *label){ fprintf(stderr, "remsync: %s: %s\n", label, strerror(errno)); excode= 1;}void fatal(const char *label){ report(label); exit(1);}void *allocate(void *mem, size_t size){ if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil) { fprintf(stderr, "remsync: Out of memory: %s\n", strerror(errno)); exit(1); } return mem;}void deallocate(void *mem){ if (mem != nil) free(mem);}/* One needs to slowly forget two sets of objects: for the code that reads * the state file, and for the code that traverses trees. */int keep;#define KEEP_STATE 0#define KEEP_TRAVERSE 1void forget(void *mem)/* Some objects must be deleted in time, but not just yet. */{ static void *death_row[2][50]; static void **dp[2]= { death_row[0], death_row[1] }; deallocate(*dp[keep]); *dp[keep]++= mem; if (dp[keep] == arraylimit(death_row[keep])) dp[keep]= death_row[keep];}char *copystr(const char *s){ char *c= allocate(nil, (strlen(s) + 1) * sizeof(c[0])); strcpy(c, s); return c;}typedef struct pathname { char *path; /* The actual pathname. */ size_t idx; /* Index for the terminating null byte. */ size_t lim; /* Actual length of the path array. */} pathname_t;void path_init(pathname_t *pp)/* Initialize a pathname to the null string. */{ pp->path= allocate(nil, (pp->lim= 16) * sizeof(pp->path[0])); pp->path[pp->idx= 0]= 0;}void path_add(pathname_t *pp, const char *name)/* Add a component to a pathname. */{ size_t lim; char *p; int slash; lim= pp->idx + strlen(name) + 2; if (lim > pp->lim) { pp->lim= lim + lim/2; /* add an extra 50% growing space. */ pp->path= allocate(pp->path, pp->lim * sizeof(pp->path[0])); } p= pp->path + pp->idx; slash= (pp->idx > 0); if (pp->idx == 1 && p[-1] == '/') p--; while (*name != 0) { if (*name == '/') { slash= 1; } else { if (slash) { *p++ = '/'; slash= 0; } *p++= *name; } name++; } if (slash && p == pp->path) *p++= '/'; *p = 0; pp->idx= p - pp->path;}void path_trunc(pathname_t *pp, size_t didx)/* Delete part of a pathname to a remembered length. */{ pp->path[pp->idx= didx]= 0;}#if kept_for_comments_onlyconst char *path_name(const pathname_t *pp)/* Return the actual name as a char array. */{ return pp->path;}size_t path_length(const pathname_t *pp)/* The length of the pathname. */{ return pp->idx;}void path_drop(pathname_t *pp)/* Release the storage occupied by the pathname. */{ free(pp->path);}#endif#define path_name(pp) ((const char *) (pp)->path)#define path_length(pp) ((pp)->idx)#define path_drop(pp) free((void *) (pp)->path)typedef struct namelist { /* Obviously a list of names. */ struct namelist *next; char *name;} namelist_t;char *rdlink(const char *link, off_t size)/* Look where "link" points. */{ static char *path= nil; static size_t len= 0; size_t n; if (len <= size) { path= allocate(path, (len= size * 2) * sizeof(path[0])); } if ((n= readlink(link, path, len)) == -1) return nil; path[n]= 0; return path;}void sort(namelist_t **anl)/* A stable mergesort disguised as line noise. Must be called like this: * if (L!=nil && L->next!=nil) sort(&L); */{ /* static */ namelist_t *nl1, **mid; /* Need not be local */ namelist_t *nl2; nl1= *(mid= &(*anl)->next); do { if ((nl1= nl1->next) == nil) break; mid= &(*mid)->next; } while ((nl1= nl1->next) != nil); nl2= *mid; *mid= nil; if ((*anl)->next != nil) sort(anl); if (nl2->next != nil) sort(&nl2); nl1= *anl; for (;;) { if (strcmp(nl1->name, nl2->name)<=0) { if ((nl1= *(anl= &nl1->next)) == nil) { *anl= nl2; break; } } else { *anl= nl2; nl2= *(anl= &nl2->next); *anl= nl1; if (nl2 == nil) break; } }}namelist_t *collect(const char *dir)/* Return a sorted list of directory entries. Returns null with errno != 0 * on error. */{ namelist_t *names, **pn= &names; DIR *dp; struct dirent *entry; if ((dp= opendir(dir)) == nil) return nil; while ((entry= readdir(dp)) != nil) { if (entry->d_name[0] == '.' && (entry->d_name[1] == 0 || (entry->d_name[1] == '.' && entry->d_name[2] == 0))) { continue; } *pn= allocate(nil, sizeof(**pn)); (*pn)->name= copystr(entry->d_name); pn= &(*pn)->next; } closedir(dp); *pn= nil; errno= 0; if (names != nil && names->next != nil) sort(&names); return names;}char *pop_name(namelist_t **names)/* Return one name of a name list. */{ char *name; namelist_t *junk; junk= *names; *names= junk->next; name= junk->name; deallocate(junk); forget(name); return name;}typedef enum filetype { /* The files we know about. */ F_DIR, F_FILE, F_BLK, F_CHR, F_PIPE, F_LINK} filetype_t;typedef struct entry { /* One file. */ int depth; /* Depth in directory tree. */ const char *name; /* Name of entry. */ const char *path; /* Path name. */ int ignore; /* Ignore this entry (errno number.) */ unsigned long fake_ino; /* Fake inode number for hard links. */ int linked; /* Is the file hard linked? */ int lastlink; /* Is it the last link? */ char *link; /* Where a (sym)link points to. */ filetype_t type; mode_t mode; /* Not unlike those in struct stat. */ uid_t uid; gid_t gid; off_t size; time_t mtime; dev_t rdev;} entry_t;void linked(entry_t *entry, struct stat *stp)/* Return an "inode number" if a file could have links (link count > 1). * Also return a path to the first link if you see the file again. */{ static unsigned long new_fake_ino= 0; static struct links { struct links *next; char *path; ino_t ino; dev_t dev; nlink_t nlink; unsigned long fake_ino; } *links[1024]; struct links **plp, *lp; entry->linked= entry->lastlink= 0; entry->fake_ino= 0; entry->link= nil; if (S_ISDIR(stp->st_mode) || stp->st_nlink < 2) return; plp= &links[stp->st_ino % arraysize(links)]; while ((lp= *plp) != nil && (lp->ino != stp->st_ino || lp->dev != stp->st_dev)) plp= &lp->next; if (lp == nil) { /* New file, store it with a new fake inode number. */ *plp= lp= allocate(nil, sizeof(*lp)); lp->next= nil; lp->path= copystr(entry->path); lp->ino= stp->st_ino; lp->dev= stp->st_dev; lp->nlink= stp->st_nlink; lp->fake_ino= ++new_fake_ino; } else { entry->link= lp->path; entry->linked= 1; } entry->fake_ino= lp->fake_ino; if (--lp->nlink == 0) { /* No need to remember this one, no more links coming. */ *plp= lp->next; forget(lp->path); deallocate(lp); entry->lastlink= 1; }}char *tree; /* Tree to work on. */FILE *statefp; /* State file. */char *state_file;FILE *difffp; /* File of differences. */char *diff_file;entry_t *traverse(void)/* Get one name from the directory tree. */{ static int depth; static pathname_t path; static entry_t entry; static namelist_t **entries; static size_t *trunc; static size_t deep; static namelist_t *newentries; struct stat st;recurse: keep= KEEP_TRAVERSE; if (deep == 0) { /* Initialize for the root of the tree. */ path_init(&path); path_add(&path, tree); entries= allocate(nil, 1 * sizeof(entries[0])); entries[0]= allocate(nil, sizeof(*entries[0])); entries[0]->next= nil; entries[0]->name= copystr("/"); trunc= allocate(nil, 1 * sizeof(trunc[0])); trunc[0]= path_length(&path); deep= 1; } else if (newentries != nil) { /* Last entry was a directory, need to go down. */ if (entry.ignore) { /* Ouch, it is to be ignored! */ while (newentries != nil) (void) pop_name(&newentries); goto recurse; } if (++depth == deep) { deep++; entries= allocate(entries, deep * sizeof(entries[0])); trunc= allocate(trunc, deep * sizeof(trunc[0])); } entries[depth]= newentries; newentries= nil; trunc[depth]= path_length(&path); } else { /* Pop up out of emptied directories. */ while (entries[depth] == nil) { if (depth == 0) return nil; /* Back at the root. */ /* Go up one level. */ depth--; } } entry.name= pop_name(&entries[depth]); path_trunc(&path, trunc[depth]); path_add(&path, entry.name); if (depth == 0) { entry.path= "/"; } else { entry.path= path_name(&path) + trunc[0]; if (entry.path[0] == '/') entry.path++; } entry.depth= depth; entry.ignore= 0; if (lstat(path_name(&path), &st) < 0) { if (depth == 0 || errno != ENOENT) { /* Something wrong with this entry, complain about * it and ignore it further. */ entry.ignore= errno; report(path_name(&path)); return &entry; } else { /* Entry strangely nonexistent; simply continue. */ goto recurse; } } /* Don't cross mountpoints if -x is set. */ if (xflag) { if (xdev == NO_DEVICE) xdev= st.st_dev; if (st.st_dev != xdev) { /* Ignore the mountpoint. */ entry.ignore= EXDEV; return &entry; } } entry.mode= st.st_mode & 07777; entry.uid= st.st_uid; entry.gid= st.st_gid; entry.size= st.st_size; entry.mtime= st.st_mtime; entry.rdev= st.st_rdev; linked(&entry, &st); if (S_ISDIR(st.st_mode)) { /* A directory. */ entry.type= F_DIR; /* Gather directory entries for the next traverse. */ if ((newentries= collect(path_name(&path))) == nil && errno != 0) { entry.ignore= errno; report(path_name(&path)); } } else if (S_ISREG(st.st_mode)) { /* A plain file. */ entry.type= F_FILE; } else if (S_ISBLK(st.st_mode)) { /* A block special file. */ entry.type= F_BLK; } else if (S_ISCHR(st.st_mode)) { /* A character special file. */ entry.type= F_CHR; } else if (S_ISFIFO(st.st_mode)) { /* A named pipe. */ entry.type= F_PIPE; } else if (S_ISLNK(st.st_mode)) { /* A symbolic link. */ entry.type= F_LINK; if ((entry.link= rdlink(path_name(&path), st.st_size)) == nil) { entry.ignore= errno; report(path_name(&path)); } } else { /* Unknown type of file. */ entry.ignore= EINVAL; } return &entry;}void checkstate(void){ if (ferror(statefp)) fatal(state_file);}void indent(int depth)/* Provide indentation to show directory depth. */{ int n= BASE_INDENT * (depth - 1); while (n >= 8) { if (putc('\t', statefp) == EOF) checkstate(); n-= 8; } while (n > 0) { if (putc(' ', statefp) == EOF) checkstate(); n--; }}int print_name(FILE *fp, const char *name)/* Encode a name. */{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -