home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2048 / mkshadow.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  9.0 KB  |  314 lines

  1. /*
  2.  * Usage: mkshadow [-X exclude_file] [-x exclude_pattern] ... MASTER
  3.  * Makes the current directory be a "shadow copy" of MASTER.
  4.  * Sort of like a recursive copy of MASTER to .
  5.  * However, symbolic links are used instead of actually
  6.  * copying (non-directory) files.
  7.  * Also, directories named RCS are shared (with a symbolic link).
  8.  * Warning messages are printed for files (and directories) in .
  9.  * that don't match a corresponding file in MASTER (though
  10.  * symbolic links are silently removed).
  11.  * Also, a warning message is printed for non-directory files
  12.  * under . that are  not symbolic links.
  13.  *
  14.  * Files and directories can be excluded from the sharing
  15.  * with the -X and -x flags. The flag `-x pattern' (or `-xpattern')
  16.  * means that mkshadow should ignore any file whose name matches
  17.  * the pattern. The pattern is a "globbing" pattern, i.e. the
  18.  * characters *?[^-] are interpreted as by the shell.
  19.  * If the pattern contains a '/' is is matched against the complete
  20.  * current path (relative to '.'); otherwise, it is matched
  21.  * against the last component of the path.
  22.  * A `-X filename' flag means to read a set of exclusion patterns
  23.  * from the named file, one pattern to a line.
  24.  *
  25.  * Author: Per Bothner. bothner@cs.wisc.edu. November 1990.
  26.  */
  27.  
  28. /* Wildcard matching routines.
  29.    Copyright (C) 1990 Per Bothner.
  30.  
  31. This file is part of mkshadow.
  32.  
  33. mkshadow is free software; you can redistribute it and/or modify
  34. it under the terms of the GNU General Public License as published by
  35. the Free Software Foundation; either version 1, or (at your option)
  36. any later version.
  37.  
  38. GNU Tar is distributed in the hope that it will be useful,
  39. but WITHOUT ANY WARRANTY; without even the implied warranty of
  40. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  41. GNU General Public License for more details.
  42.  
  43. You should have received a copy of the GNU General Public License
  44. along with GNU Tar; see the file COPYING.  If not, write to
  45. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  46.  
  47. #include <sys/param.h>
  48. #include <sys/types.h>
  49. #include <stdio.h>
  50. #ifndef USG
  51. #include <strings.h>
  52. #else
  53. #include <string.h>
  54. #define index strchr
  55. #define rindex strrchr
  56. #endif
  57. #include <sys/stat.h>
  58. #ifndef S_IFLNK
  59. #define lstat stat
  60. #endif
  61. #ifndef MAXPATHLEN
  62. #define MAXPATHLEN 1024
  63. #endif
  64. #include "errno.h"
  65. #ifndef errno
  66. extern int errno;
  67. #endif
  68.  
  69. extern char * savedir();
  70.  
  71. fatal(msg)
  72.      char *msg;
  73. {
  74.     if (errno) perror(msg ? msg : "");
  75.     else if (msg) fprintf(stderr, "mkshadow: %s\n", msg);
  76.     exit(-1);
  77. }
  78.  
  79. char master_buffer[MAXPATHLEN];
  80. char current_buffer[MAXPATHLEN];
  81.  
  82. void bad_args(msg)
  83. {
  84.     if (msg) fprintf(stderr, "%s\n", msg);
  85.     fprintf(stderr, 
  86.       "usage: mkshadow [-X exclude_file] [-x exclude_pattern] master_src\n");
  87.     exit(-1);
  88. }
  89.  
  90. int exclude_count = 0;
  91. char **exclude_patterns = NULL;
  92. int exclude_limit = 0;
  93.  
  94. void add_exclude(pattern)
  95.     char *pattern;
  96. {
  97.     if (exclude_limit == 0) {
  98.     exclude_limit = 100;
  99.     exclude_patterns = (char**)malloc(exclude_limit * sizeof(char*));
  100.     } else if (exclude_count + 1 >= exclude_limit) {
  101.     exclude_limit += 100;
  102.     exclude_patterns = (char**)realloc(exclude_patterns, 
  103.                        exclude_limit * sizeof(char*));
  104.     }
  105.     exclude_patterns[exclude_count] = pattern;
  106.     exclude_count++;
  107. }
  108.  
  109. void add_exclude_file(name)
  110.      char *name;
  111. {
  112.     char buf[MAXPATHLEN];
  113.     FILE *file = fopen(name, "r");
  114.     if (file == NULL) fatal("failed to find -X (exclude) file");
  115.     for (;;) {
  116.     int len;
  117.     char *str = fgets(buf, MAXPATHLEN, file);
  118.     if (str == NULL) break;
  119.     len = strlen(str);
  120.     if (len && str[len-1] == '\n') str[--len] = 0;
  121.     if (!len) continue;
  122.     str = (char*)malloc(len+1);
  123.     strcpy(str, buf);
  124.     add_exclude(str);
  125.     }
  126.     fclose(file);
  127. }
  128.  
  129. main(argc, argv)
  130.      char **argv;
  131. {
  132.     char *root_name = NULL;
  133.     int i;
  134.     for (i = 1; i < argc; i++) {
  135.     if (argv[i][0] == '-') {
  136.         switch(argv[i][1]) {
  137.           case 'X':
  138.         if (argv[i][2]) add_exclude_file(&argv[i][2]);
  139.         else if (++i >= argc) bad_args(NULL);
  140.         else add_exclude_file(argv[i]);
  141.         break;
  142.           case 'x':
  143.         if (argv[i][2]) add_exclude(&argv[i][2]);
  144.         else if (++i >= argc) bad_args(NULL);
  145.         else add_exclude(argv[i]);
  146.         break;
  147.           default:
  148.         bad_args(NULL);
  149.         }
  150.     } else if (root_name) bad_args(NULL);
  151.     else root_name = argv[i];
  152.     }
  153.     if (root_name == NULL) bad_args(NULL);
  154.     strcpy(current_buffer, ".");
  155.     strcpy(master_buffer, root_name);
  156.     DoCopy(master_buffer, current_buffer);
  157.     return 0;
  158. }
  159.  
  160. int compare_strings(ptr1, ptr2)
  161.      char **ptr1, **ptr2;
  162. {
  163.     return strcmp(*ptr1, *ptr2);
  164. }
  165.  
  166. /* Get a sorted NULL_terminator array of (char*) using 'names'
  167.  * (created by save_dir) as data.
  168.  */
  169. char ** get_name_pointers(names)
  170.      char *names;
  171. {
  172.     int n_names = 0;
  173.     int names_buf_size = 64;
  174.     char *namep;
  175.     char ** pointers = (char**)malloc(names_buf_size * sizeof(char*));
  176.     if (!names || !pointers) fatal("virtual memory exhausted");
  177.  
  178.     for (namep = names; *namep; namep += strlen(namep) + 1) {
  179.     if (n_names + 1 >= names_buf_size) {
  180.         names_buf_size *= 2;
  181.         pointers = (char**)realloc(pointers,
  182.                        names_buf_size * sizeof(char*));
  183.         if (!pointers) fatal("virtual memory exhausted");
  184.     }
  185.     pointers[n_names++] = namep;
  186.     }
  187.     pointers[n_names] = 0;
  188.     qsort(pointers, n_names, sizeof(char*), compare_strings);
  189.     return pointers;
  190. }
  191.  
  192. DoCopy(master, current)
  193.      char *master;
  194.      char *current;
  195. {
  196.     struct stat stat_master, stat_current;
  197.     char **master_pointer, **current_pointer;
  198.     char **master_names, **current_names;
  199.     char *master_end, *current_end;
  200.     char *master_name_buf, *current_name_buf;
  201.     master_end = master + strlen(master);
  202.     current_end = current + strlen(current);
  203.  
  204.     /* Get rid of terminal '/' */
  205.     if (master_end[-1] == '/' && master != master_end - 1)
  206.     *--master_end = 0;
  207.     if (current_end[-1] == '/' && current != current_end - 1)
  208.     *--current_end = 0;
  209.  
  210.     master_name_buf = savedir(master, 500);
  211.     current_name_buf = savedir(current, 500);
  212.  
  213.     master_names = get_name_pointers(master_name_buf);
  214.     current_names = get_name_pointers(current_name_buf);
  215.  
  216.     master_pointer = master_names;
  217.     current_pointer = current_names;
  218.     for (;;) {
  219.     int cmp, ipat;
  220.     int in_master, in_current;
  221.     char *cur_name;
  222.     if (*master_pointer == NULL && *current_pointer == NULL)
  223.         break;
  224.     if (*master_pointer == NULL) cmp = 1;
  225.     else if (*current_pointer == NULL) cmp = -1;
  226.     else cmp = strcmp(*master_pointer, *current_pointer);
  227.     if (cmp < 0) { /* file only exists in master directory */
  228.         in_master = 1; in_current = 0;
  229.     } else if (cmp == 0) { /* file exists in both directories */
  230.         in_master = 1; in_current = 1;
  231.     } else { /* file only exists in current directory */
  232.         in_current = 1; in_master = 0;
  233.     }
  234.     cur_name = in_master ? *master_pointer : *current_pointer;
  235.     sprintf(master_end, "/%s", cur_name);
  236.     sprintf(current_end, "/%s", cur_name);
  237.     for (ipat = 0; ipat < exclude_count; ipat++) {
  238.         char *pat = exclude_patterns[ipat];
  239.         char *cur;
  240.         if (index(pat, '/')) cur = current + 2; /* Skip initial "./" */
  241.         else cur = cur_name;
  242.         if (wildmat(cur, pat)) goto skip;
  243.     }
  244.     if (in_master)
  245.         if (lstat(master, &stat_master) != 0) fatal("stat failed");
  246.     if (in_current)
  247.         if (lstat(current, &stat_current) != 0) fatal("stat failed");
  248.     if (in_current && !in_master) {
  249.         if ((stat_current.st_mode & S_IFMT) == S_IFLNK)
  250.         if (unlink(current)) {
  251.             fprintf(stderr, "Failed to remove symbolic link %s.\n",
  252.                 current);
  253.         }
  254.         else
  255.             fprintf(stderr, "Removed symbolic link %s.\n",
  256.                 current);
  257.         else {
  258.         fprintf(stderr,
  259.             "The file %s does not exist in the master tree.\n",
  260.             current);
  261.         }
  262.     }
  263.     else if ((stat_master.st_mode & S_IFMT) == S_IFDIR
  264.          && strcmp(cur_name, "RCS") != 0) {
  265.         if (!in_current) {
  266.         if (mkdir(current, 0775)) fatal("mkdir failed");
  267.         }
  268.         else if (stat(current, &stat_current)) fatal("stat failed");
  269.         if (!in_current || stat_current.st_dev != stat_master.st_dev
  270.         || stat_current.st_ino != stat_master.st_ino)
  271.         DoCopy(master, current);
  272.         else
  273.         fprintf(stderr, "Link %s is the same as directory %s.\n",
  274.             current, master);
  275.     }
  276.     else {
  277.         if (!in_current) {
  278.         if (symlink(master, current)) {
  279.             fprintf(stderr, "Failed to create symbolic link %s->%s\n",
  280.                 current, master);
  281.             exit (-1);
  282.         }
  283.         } else if ((stat_current.st_mode & S_IFMT) != S_IFLNK) {
  284.         fprintf(stderr, "Existing file %s is not a symbolc link.\n",
  285.             current);
  286.         } else {
  287.         if (stat(current, &stat_current) || stat(master, &stat_master))
  288.             fatal("stat failed");
  289.         if (stat_current.st_dev != stat_master.st_dev
  290.             || stat_current.st_ino != stat_master.st_ino) {
  291.             fprintf(stderr, "Fixing incorrect symbolic link %s.\n",
  292.                 current);
  293.             if (unlink(current)) {
  294.             fprintf(stderr, "Failed to remove symbolic link %s.\n",
  295.                 current);
  296.             }
  297.             else if (symlink(master, current)) {
  298.             fprintf(stderr,
  299.                 "Failed to create symbolic link %s->%s\n",
  300.                 current, master);
  301.             exit (-1);
  302.             }
  303.         }
  304.         }
  305.     }
  306.       skip:
  307.     if (in_master) master_pointer++;
  308.     if (in_current) current_pointer++;
  309.     }
  310.  
  311.     free(master_names); free(current_names);
  312.     free(master_name_buf); free(current_name_buf);
  313. }
  314.