📄 remsync.c
字号:
const char *p; int c; for (p= name; (c= (unsigned char) *p) != 0; p++) { if (c <= ' ' || c == '\\') { fprintf(fp, "\\%03o", c); if (ferror(fp)) return 0; } else { if (putc(c, fp) == EOF) return 0; } } return 1;}void mkstatefile(void)/* Make a state file out of the directory tree. */{ entry_t *entry; while ((entry= traverse()) != nil) { indent(entry->depth); if (!print_name(statefp, entry->name)) checkstate(); if (entry->ignore) { fprintf(statefp, "\tignore (%s)\n", strerror(entry->ignore)); checkstate(); continue; } switch (entry->type) { case F_DIR: fprintf(statefp, "\td%03o %u %u", (unsigned) entry->mode, (unsigned) entry->uid, (unsigned) entry->gid); break; case F_FILE: fprintf(statefp, "\t%03o %u %u %lu %lu", (unsigned) entry->mode, (unsigned) entry->uid, (unsigned) entry->gid, (unsigned long) entry->size, (unsigned long) entry->mtime); break; case F_BLK: case F_CHR: fprintf(statefp, "\t%c%03o %u %u %x", entry->type == F_BLK ? 'b' : 'c', (unsigned) entry->mode, (unsigned) entry->uid, (unsigned) entry->gid, (unsigned) entry->rdev); break; case F_PIPE: fprintf(statefp, "\tp%03o %u %u", (unsigned) entry->mode, (unsigned) entry->uid, (unsigned) entry->gid); break; case F_LINK: fprintf(statefp, "\t-> "); checkstate(); (void) print_name(statefp, entry->link); break; } checkstate(); if (entry->fake_ino != 0) fprintf(statefp, " %lu", entry->fake_ino); if (entry->lastlink) fprintf(statefp, " last"); if (fputc('\n', statefp) == EOF) checkstate(); } fflush(statefp); checkstate();}char *read1line(FILE *fp)/* Read one line from a file. Return null on EOF or error. */{ static char *line; static size_t len; size_t idx; int c; if (len == 0) line= allocate(nil, (len= 16) * sizeof(line[0])); idx= 0; while ((c= getc(fp)) != EOF && c != '\n') { if (c < '\t') { /* Control characters are not possible. */ fprintf(stderr, "remsync: control character in data file!\n"); exit(1); } line[idx++]= c; if (idx == len) { line= allocate(line, (len*= 2) * sizeof(line[0])); } } if (c == EOF) { if (ferror(fp)) return nil; if (idx == 0) return nil; } line[idx]= 0; return line;}void getword(char **pline, char **parg, size_t *plen)/* Get one word from a line, interpret octal escapes. */{ char *line= *pline; char *arg= *parg; size_t len= *plen; int i; int c; size_t idx; idx= 0; while ((c= *line) != 0 && c != ' ' && c != '\t') { line++; if (c == '\\') { c= 0; for (i= 0; i < 3; i++) { if ((unsigned) (*line - '0') >= 010) break; c= (c << 3) | (*line - '0'); line++; } } arg[idx++]= c; if (idx == len) arg= allocate(arg, (len*= 2) * sizeof(arg[0])); } arg[idx]= 0; *pline= line; *parg= arg; *plen= len;}void splitline(char *line, char ***pargv, size_t *pargc)/* Split a line into an array of words. */{ static char **argv; static size_t *lenv; static size_t len; size_t idx; idx= 0; for (;;) { while (*line == ' ' || *line == '\t') line++; if (*line == 0) break; if (idx == len) { len++; argv= allocate(argv, len * sizeof(argv[0])); lenv= allocate(lenv, len * sizeof(lenv[0])); argv[idx]= allocate(nil, 16 * sizeof(argv[idx][0])); lenv[idx]= 16; } getword(&line, &argv[idx], &lenv[idx]); idx++; } *pargv= argv; *pargc= idx;}int getattributes(entry_t *entry, int argc, char **argv)/* Convert state or difference file info into file attributes. */{ int i; int attr;#define A_MODE1 0x01 /* Some of these attributes follow the name */#define A_MODE 0x02#define A_OWNER 0x04#define A_SIZETIME 0x08#define A_DEV 0x10#define A_LINK 0x20 switch (argv[0][0]) { case 'd': /* Directory. */ entry->type= F_DIR; attr= A_MODE1 | A_OWNER; break; case 'b': /* Block device. */ entry->type= F_BLK; attr= A_MODE1 | A_OWNER | A_DEV; break; case 'c': /* Character device. */ entry->type= F_CHR; attr= A_MODE1 | A_OWNER | A_DEV; break; case 'p': /* Named pipe. */ entry->type= F_PIPE; attr= A_MODE1 | A_OWNER; break; case '-': /* Symlink. */ entry->type= F_LINK; attr= A_LINK; break; default: /* Normal file. */ entry->type= F_FILE; attr= A_MODE | A_OWNER | A_SIZETIME; } if (attr & (A_MODE | A_MODE1)) { entry->mode= strtoul(argv[0] + (attr & A_MODE1), nil, 010); } i= 1; if (attr & A_OWNER) { if (i + 2 > argc) return 0; entry->uid= strtoul(argv[i++], nil, 10); entry->gid= strtoul(argv[i++], nil, 10); } if (attr & A_SIZETIME) { if (i + 2 > argc) return 0; entry->size= strtoul(argv[i++], nil, 10); entry->mtime= strtoul(argv[i++], nil, 10); } if (attr & A_DEV) { if (i + 1 > argc) return 0; entry->rdev= strtoul(argv[i++], nil, 0x10); } if (attr & A_LINK) { if (i + 1 > argc) return 0; entry->link= argv[i++]; } entry->linked= entry->lastlink= 0; if (i < argc) { /* It has a fake inode number, so it is a hard link. */ static struct links { /* List of hard links. */ struct links *next; unsigned long fake_ino; char *path; } *links[1024]; struct links **plp, *lp; unsigned long fake_ino; fake_ino= strtoul(argv[i++], nil, 10); plp= &links[fake_ino % arraysize(links)]; while ((lp= *plp) != nil && lp->fake_ino != fake_ino) plp= &lp->next; if (lp == nil) { /* New link. */ *plp= lp= allocate(nil, sizeof(*lp)); lp->next= nil; lp->fake_ino= fake_ino; lp->path= copystr(entry->path); } else { /* Linked to. */ entry->link= lp->path; entry->linked= 1; } if (i < argc) { if (strcmp(argv[i++], "last") != 0) return 0; /* Last hard link of a file. */ forget(lp->path); *plp= lp->next; deallocate(lp); entry->lastlink= 1; } } if (i != argc) return 0; return 1;}void state_syntax(off_t line){ fprintf(stderr, "remsync: %s: syntax error on line %lu\n", state_file, (unsigned long) line); exit(1);}entry_t *readstate(void)/* Read one entry from the state file. */{ static entry_t entry; static pathname_t path; static size_t *trunc; static size_t trunc_len; static base_indent; char *line; char **argv; size_t argc; static off_t lineno; int indent, depth;recurse: keep= KEEP_STATE; if (feof(statefp) || (line= read1line(statefp)) == nil) { checkstate(); return nil; } lineno++; /* How far is this entry indented? */ indent= 0; while (*line != 0) { if (*line == ' ') indent++; else if (*line == '\t') indent= (indent + 8) & ~7; else break; line++; } if (indent > 0 && base_indent == 0) base_indent= indent; depth= (base_indent == 0 ? 0 : indent / base_indent) + 1; if (entry.ignore && depth > entry.depth) { /* If the old directory is ignored, then so are its entries. */ goto recurse; } entry.depth= depth; splitline(line, &argv, &argc); if (argc < 2) state_syntax(lineno); if (trunc == nil) { /* The root of the tree, initialize path. */ if (argv[0][0] != '/') state_syntax(lineno); path_init(&path); path_add(&path, "/"); trunc= allocate(nil, (trunc_len= 16) * sizeof(trunc[0])); /* The root has depth 0. */ entry.depth= 0; trunc[0]= 0; } else { if (entry.depth > trunc_len) { trunc= allocate(trunc, (trunc_len*= 2) * sizeof(trunc[0])); } path_trunc(&path, trunc[entry.depth - 1]); path_add(&path, argv[0]); trunc[entry.depth]= path_length(&path); } entry.path= path_name(&path); entry.name= argv[0]; entry.link= nil; if ((entry.ignore= strcmp(argv[1], "ignore") == 0)) { return &entry; } if (!getattributes(&entry, argc - 1, argv + 1)) state_syntax(lineno); return &entry;}void checkdiff(void){ if (ferror(difffp)) fatal(diff_file);}enum { DELETE, REPLACE, COPY, SIMILAR, EQUAL, ADD }compare(entry_t *remote, entry_t *local)/* Compare the local and remote entries and tell what need to be done. */{ int cmp; /* Surplus entries? */ if (local == nil) return DELETE; if (remote == nil) return ADD; /* Extra directory entries? */ if (remote->depth > local->depth) return DELETE; if (local->depth > remote->depth) return ADD; /* Compare names. */ cmp= strcmp(remote->name, local->name); if (cmp < 0) return DELETE; if (cmp > 0) return ADD; /* The files have the same name. Ignore one, ignore the other. */ if (remote->ignore || local->ignore) { remote->ignore= local->ignore= 1; return EQUAL; } /* Reasons for replacement? */ if (remote->type != local->type) return REPLACE; /* Should be hard linked to the same file. */ if (remote->linked || local->linked) { if (!remote->linked || !local->linked) return REPLACE; if (strcmp(remote->link, local->link) != 0) return REPLACE; } switch (remote->type) { case F_FILE: if (uflag) { if (remote->mtime < local->mtime) return COPY; } else { if (remote->size != local->size || remote->mtime != local->mtime) return COPY; } goto check_modes; case F_BLK: case F_CHR: if (remote->rdev != local->rdev) return REPLACE; goto check_modes; case F_DIR: case F_PIPE: check_modes: if (remote->mode != local->mode || remote->uid != local->uid || remote->gid != local->gid) return SIMILAR; break; case F_LINK: if (strcmp(remote->link, local->link) != 0) return REPLACE; break; } return EQUAL;}void delete(entry_t *old)/* Emit an instruction to remove an entry. */{ if (old->ignore) return; if (uflag) return; fprintf(difffp, "rm "); checkdiff(); if (!print_name(difffp, old->path)) checkdiff(); if (putc('\n', difffp) == EOF) checkdiff(); if (vflag) fprintf(stderr, "rm %s\n", old->path);}void change_modes(entry_t *old, entry_t *new)/* Emit an instruction to change the attributes of an entry. */{ if (new->ignore) return; fprintf(difffp, "chmod "); checkdiff(); if (!print_name(difffp, new->path)) checkdiff(); fprintf(difffp, " %03o %u %u\n", (unsigned) new->mode, (unsigned) new->uid, (unsigned) new->gid); checkdiff(); if (vflag && old->mode != new->mode) { fprintf(stderr, "chmod %s %03o %u %u\n", new->path, (unsigned) new->mode, (unsigned) new->uid, (unsigned) new->gid); }}int cat(int f, off_t size)/* Include the contents of a file in the differences file. */{ ssize_t n; unsigned char buf[1024 << sizeof(int)]; unsigned char *p; int c; if (Dflag) return 1; /* Debug: Don't need the file contents. */ while ((n= read(f, buf, sizeof(buf))) > 0) { p= buf; do { if (size == 0) { /* File suddenly larger. */ errno= EINVAL; return 0; } c= *p++; if (putc(c, difffp) == EOF) checkdiff(); size--; } while (--n != 0); } if (size > 0) { int err= errno; /* File somehow shrunk, pad it out. */ do { if (putc(0, difffp) == EOF) checkdiff(); } while (--size != 0); errno= n == 0 ? EINVAL : err; n= -1; } return n == 0;}void add(entry_t *old, entry_t *new)/* Emit an instruction to add an entry. */{ pathname_t file; int f; if (new->ignore) return; if (new->linked) { /* This file is to be a hard link to an existing file. */ fprintf(difffp, "ln "); checkdiff(); if (!print_name(difffp, new->link)) checkdiff(); if (fputc(' ', difffp) == EOF) checkdiff(); if (!print_name(difffp, new->path)) checkdiff(); if (fputc('\n', difffp) == EOF) checkdiff(); if (vflag) { fprintf(stderr, "ln %s %s\n", new->link, new->path); } return; } /* Add some other type of file. */ fprintf(difffp, "add "); checkdiff(); if (!print_name(difffp, new->path)) checkdiff(); switch (new->type) { case F_DIR:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -