home *** CD-ROM | disk | FTP | other *** search
/ The UNIX CD Bookshelf / OREILLY_TUCB_UNIX_CD.iso / upt / examples / SOURCES / REN / AMEFILES.Z / AMEFILES / ren.c < prev   
Encoding:
C/C++ Source or Header  |  1998-07-24  |  11.1 KB  |  549 lines

  1. #include <stdio.h>
  2. #include <sys/file.h>
  3. #include <sys/types.h>
  4. #include <sys/dir.h>
  5.  
  6. #define MAXWILD 20
  7. #define MAXREP 40
  8.  
  9. typedef struct rep{
  10.     int ftorep;
  11.     char *repname;
  12.     struct rep *dofirst;
  13.     int mustdel;
  14.     int status;
  15.     struct rep *nextrep;
  16. } REP;
  17.  
  18. #define DO 0
  19. #define FORGET 1
  20. #define DONE 2
  21.  
  22. #define ASKDEL 0
  23. #define QUIETDEL 1
  24. #define NODEL 2
  25. #define ABORTDEL 3
  26.  
  27. extern int alphasort();
  28. extern int scandir();
  29. extern qsort();
  30.  
  31. static procargs();
  32. static int procdir();
  33. static int checkpats();
  34. static int getreps();
  35. static int scan();
  36. static char *makerep();
  37. static checkcollisons();
  38. static int mycmp();
  39. static checkdeletes();
  40. static int bsearch();
  41. static char** breakcycles();
  42. static dorenames();
  43.  
  44. static char tempprefix[] = "renTMP";
  45.  
  46. main(argc, argv)
  47.     int argc;
  48.     char *(argv[]);
  49. {
  50.     char *from_pat, *to_pat, *path;
  51.     int verbose, delstyle;
  52.     int nfils, nreps;
  53.     struct direct **dot;
  54.     REP *reps, **filrep;
  55.     char **tempnames;
  56.  
  57.     procargs(argc, argv, &verbose, &delstyle, &from_pat, &to_pat, &path);
  58.     checkpats(from_pat, to_pat);
  59.     nfils = procdir(path, &dot, &filrep);
  60.     nreps = getreps(dot, nfils, from_pat, to_pat, filrep, &reps);
  61.     checkcollisons(reps, nreps);
  62.     checkdeletes(reps, dot, filrep, nfils, delstyle);
  63.     tempnames = breakcycles(reps, nreps, dot);
  64.     dorenames(reps, dot, tempnames, verbose);
  65.     return(0);
  66. }
  67.  
  68.  
  69. static procargs(argc, argv, pverbose, pdelstyle, pfrom_pat, pto_pat, ppath)
  70.     int argc;
  71.     char *(argv[]);
  72.     int *pverbose, *pdelstyle;
  73.     char **pfrom_pat, **pto_pat, **ppath;
  74. {
  75.     char *p;
  76.     int arg1;
  77.  
  78.     *pverbose = 0;
  79.     *pdelstyle = ASKDEL;
  80.     for (arg1 = 1; arg1 < argc && *(argv[arg1]) == '-'; arg1++)
  81.         for (p = argv[arg1]+1; *p != '\0'; p++)
  82.             if (*p == 'v')
  83.                 *pverbose = 1;
  84.             else if (*p == 'd')
  85.                 *pdelstyle = QUIETDEL;
  86.             else if (*p == 'k')
  87.                 *pdelstyle = NODEL;
  88.             else if (*p == 'a')
  89.                 *pdelstyle = ABORTDEL;
  90.             else {
  91.                 fputs("Illegal option -", stderr);
  92.                 fputc(*p, stderr);
  93.                 fputc('\n', stderr);
  94.                 exit(1);
  95.             }
  96.  
  97.     if (argc - arg1 != 2) {
  98.         fputs(
  99.             "Usage: ren [-d|-k|-a] [-v] [path/]search_pattern replacement_pattern\n",
  100.             stderr);
  101.         fputs("\nSearch patterns containing wildcard(s) should be quoted.\n",
  102.             stderr);
  103.         fputs("Put #n into the replacement pattern to insert the string\n",
  104.             stderr);
  105.         fputs("matched by the n'th wildcard in the search pattern.\n", stderr);
  106.         exit(1);
  107.     }
  108.  
  109.     *ppath = argv[arg1];
  110.     *pto_pat = argv[arg1 + 1];
  111.     for (
  112.         *pfrom_pat = *ppath + strlen(*ppath);
  113.         **pfrom_pat != '/' && *pfrom_pat != *ppath;
  114.         --(*pfrom_pat)
  115.     ) {}
  116.     if (**pfrom_pat == '/') {
  117.         **pfrom_pat = '\0';
  118.         if (*pfrom_pat == *ppath)
  119.             *ppath = "/";
  120.         (*pfrom_pat)++;
  121.     }
  122.     else
  123.         *ppath = ".";
  124. }
  125.  
  126.  
  127. static checkpats(from_pat, to_pat)
  128.     char *from_pat, *to_pat;
  129. {
  130.     char *p;
  131.     int nwilds;
  132.  
  133.     for (nwilds = 0, p = from_pat; *p != '\0'; p++) {
  134.         if (*p == '\\') {
  135.             p++;
  136.             if (*p == '\0')
  137.                 break;
  138.         }
  139.         else if (*p == '*' || *p == '?')
  140.             nwilds++;
  141.     }
  142.  
  143.     for (p = to_pat; *p != '\0'; p++) {
  144.         if (*p == '/') {
  145.             fputs("The replacement pattern must not contain a path.\n",
  146.                 stderr);
  147.             exit(1);
  148.         }
  149.         else if (*p == '*' || *p == '?') {
  150.             fputs("No wildcards allowed in replacement pattern.\n", stderr);
  151.             fputs("Use #n to insert the substring matched\n", stderr);
  152.             fputs("by the n'th wildcard in the search pattern.\n", stderr);
  153.             exit(1);
  154.         }
  155.         else if (*p == '#') {
  156.             if (*(p+1) < '1' || *(p+1) > '9' || *(p+1) - '0' > nwilds) {
  157.                 fputc('#', stderr);
  158.                 fputc(*(p+1), stderr);
  159.                 fputs(": bad substring numer.\n", stderr);
  160.                 fputs("(Use '\\#' to get '#' in replacement string.)\n", stderr);
  161.                 exit(1);
  162.             }
  163.             p++;
  164.         }
  165.         else if (*p == '\\') {
  166.             p++;
  167.             if (*p == '\0')
  168.                 break;
  169.         }
  170.     }
  171. }
  172.  
  173.  
  174. static int procdir(path, pdot, pfilrep)
  175.     char *path;
  176.     struct direct ***pdot;
  177.     REP ***pfilrep;
  178. {
  179.     int nfils;
  180.  
  181.     if (access(path, R_OK | X_OK | W_OK) < 0) {
  182.         fputs("Read/write access denied to ", stderr);
  183.         fputs(path, stderr);
  184.         fputc('\n', stderr);
  185.         exit(1);
  186.     }
  187.     if (chdir(path) < 0) {
  188.         fputs("Strange, can not change directory to ", stderr);
  189.         fputs(path, stderr);
  190.         fputc('\n', stderr);
  191.         exit(1);
  192.     }
  193.     if ((nfils = scandir(".", pdot, NULL, alphasort)) < 0) {
  194.         fputs("Strange, can not scan ", stderr);
  195.         fputs(path, stderr);
  196.         fputc('\n');
  197.         exit(1);
  198.     }
  199.     *pfilrep = (REP **)malloc(nfils * sizeof(REP *));
  200.     return(nfils);
  201. }
  202.  
  203.  
  204.  
  205. static int getreps(dot, nfils, from_pat, to_pat, filrep, preps)
  206.     struct direct *(dot[]);
  207.     int nfils;
  208.     char *from_pat, *to_pat;
  209.     REP *(filrep[]);
  210.     REP **preps;
  211. {
  212.     char *(start[MAXWILD]);
  213.     int len[MAXWILD];
  214.     int nreps, i;
  215.     REP *cur;
  216.  
  217.     for (*preps = NULL, nreps = 0, i = 0; i < nfils; i++) {
  218.         if (
  219.             (*(dot[i]->d_name) != '.' || *from_pat == '.') &&
  220.             strcmp(dot[i]->d_name, ".") != 0 &&
  221.             strcmp(dot[i]->d_name, "..") != 0 &&
  222.             scan(from_pat, dot[i]->d_name, start, len)
  223.         ) {
  224.             cur = (REP *)malloc(sizeof(REP));
  225.             filrep[i] = cur;
  226.             cur->repname = makerep(to_pat, start, len);
  227.             cur->ftorep = i;
  228.             cur->mustdel = -1;
  229.             cur->nextrep = *preps;
  230.             cur->status = DO;
  231.             *preps = cur;
  232.             nreps++;
  233.             if (*(cur->repname) == '\0') {
  234.                 fputc('\'', stderr);
  235.                 fputs(dot[i]->d_name, stderr);
  236.                 fputs("' would have to be renamed to empty string.\n",
  237.                     stderr);
  238.                 fputs("Aborting, no renames done.\n");
  239.                 exit(1);
  240.             }
  241.         }
  242.         else
  243.             filrep[i] = NULL;
  244.     }
  245.     if (nreps == 0) {
  246.         fputs("No match.\n", stderr);
  247.         exit(1);
  248.     }
  249.     return(nreps);
  250. }
  251.  
  252.  
  253. static int scan(pat, s, start1, len1)
  254.     char *pat, *s, **start1;
  255.     int *len1;
  256. {
  257.     *start1 = 0;
  258.     while (1) {
  259.         if (*pat == '*') {
  260.             pat++;
  261.             *start1 = s;
  262.             if (*pat == '\0') {
  263.                 *len1 = strlen(s);
  264.                 return(1);
  265.             }
  266.             else {
  267.                 for (*len1=0; !scan(pat, s, start1+1, len1+1); (*len1)++, s++)
  268.                     if (*s == '\0')
  269.                         return(0);
  270.                 return(1);
  271.             }
  272.         }
  273.         else if (*pat == '\0')
  274.             return(*s == '\0');
  275.         else if (*pat == '?') {
  276.             if (*s == '\0')
  277.                 return(0);
  278.             *(start1++) = s;
  279.             *(len1++) = 1;
  280.             pat++;
  281.             s++;
  282.         }
  283.         else {
  284.             if (*pat == '\\') {
  285.                 pat++;
  286.                 if (*pat == '\0')
  287.                     return(*s == '\0');
  288.             }
  289.             if (*pat == *s) {
  290.                  pat++;
  291.                 s++;
  292.             }
  293.             else
  294.                 return(0);
  295.         }
  296.     }
  297. }
  298.  
  299.  
  300. static char *makerep(pat, start, len)
  301.     char *pat, **start;
  302.     int *len;
  303. {
  304.     int i, k;
  305.     char *q, *p, *res;
  306.     char b[MAXNAMLEN];
  307.  
  308.     p = b;
  309.     for ( ; *pat != '\0'; pat++) {
  310.         if (*pat == '#') {
  311.             pat++;
  312.             k = *pat - '1';
  313.             if (p - b + len[k] > MAXNAMLEN) {
  314.                 fputs("Replacement name too long.\n", stderr);
  315.                 exit(1);
  316.             }
  317.             for (i=0, q = start[k]; i < len[k]; i++)
  318.                 *(p++)= *(q++);
  319.         }
  320.         else {
  321.             if (*pat == '\\') {
  322.                 pat++;
  323.                 if (*pat == '\0')
  324.                     break;
  325.             }
  326.             if (p - b + 1 > MAXNAMLEN) {
  327.                 fputs("Replacement name too long.\n", stderr);
  328.                 exit(1);
  329.             }
  330.             *(p++)= *pat;
  331.         }
  332.     }
  333.     *(p++) = '\0';
  334.     res = (char *)malloc((p - b) * sizeof(char));
  335.     strcpy(res, b);
  336.     return(res);
  337. }
  338.  
  339.  
  340. static checkcollisons(reps, nreps)
  341.     REP *reps;
  342.     int nreps;
  343. {
  344.     char **repdict;
  345.     REP *cur;
  346.     int i;
  347.  
  348.     repdict = (char **)malloc(nreps * sizeof(char *));
  349.     for (i = 0, cur = reps; cur != NULL; cur = cur->nextrep)
  350.         repdict[i++] = cur->repname;
  351.     qsort(repdict, nreps, sizeof(char *), mycmp);
  352.     for (i = 0; i < nreps-1; i++)
  353.         if (strcmp(repdict[i], repdict[i+1]) == 0) {
  354.             fputs("Two or more files would have to be renamed to '",
  355.                 stderr);
  356.             fputs(repdict[i], stderr);
  357.             fputs("'.\n", stderr);
  358.             fputs("Aborting, no renames done.\n", stderr);
  359.             exit(1);
  360.         }
  361. }
  362.  
  363.  
  364. static int mycmp(pps1, pps2)
  365.     char **pps1, **pps2;
  366. {
  367.     return(strcmp(*pps1, *pps2));
  368. }
  369.  
  370.  
  371. static checkdeletes(reps, dot, filrep, nfils, delstyle)
  372.     REP *reps;
  373.     struct direct *(dot[]);
  374.     REP *(filrep[]);
  375.     int nfils, delstyle;
  376. {
  377.     int recheck, i;
  378.     REP *cur;
  379.     char doit, reply[MAXREP];
  380.  
  381.     do {
  382.         recheck = 0;
  383.         for (cur = reps; cur != NULL; cur = cur->nextrep) {
  384.             if (cur->status == FORGET)
  385.                 continue;
  386.             if ((i = bsearch(cur->repname, dot, nfils)) >= 0) {
  387.                 if (filrep[i] == NULL && cur->mustdel < 0) {
  388.                     cur->dofirst = NULL;
  389.                     if (delstyle == QUIETDEL)
  390.                         cur->mustdel = i;
  391.                     else if (delstyle == NODEL) {
  392.                         cur->status = FORGET;
  393.                         filrep[cur->ftorep] = NULL;
  394.                         recheck = 1;
  395.                     }
  396.                     else if (delstyle == ABORTDEL) {
  397.                         fputs(dot[i]->d_name, stderr);
  398.                         fputs(" would have to be removed.\n", stderr);
  399.                         fputs("Aborting, no renames done.\n", stderr);
  400.                         exit(1);
  401.                     }
  402.                     else {
  403.                         fputs(dot[cur->ftorep]->d_name, stderr);
  404.                         fputs(" -> ", stderr);
  405.                         fputs(cur->repname, stderr);
  406.                         fputs(" ; remove old ", stderr);
  407.                         fputs(dot[i]->d_name, stderr);
  408.                         fputs("? ", stderr);
  409.                         for (;;) {
  410.                             doit = *fgets(reply, MAXREP, stdin);
  411.                             if (doit ==  'Y' || doit == 'y') {
  412.                                 cur->mustdel = i;
  413.                                 break;
  414.                             }
  415.                             else if (doit == 'N' || doit == 'n') {
  416.                                 cur->status = FORGET;
  417.                                 filrep[cur->ftorep] = NULL;
  418.                                 recheck = 1;
  419.                                 break;
  420.                             }
  421.                             else
  422.                                 fputs("Yes or No? ", stderr);
  423.                         }
  424.                     }
  425.                 }
  426.                 else {
  427.                     cur->dofirst = filrep[i];
  428.                     cur->mustdel = -1;
  429.                 }
  430.             }
  431.             else {
  432.                 cur->dofirst = NULL;
  433.                 cur->mustdel = -1;
  434.             }
  435.         }
  436.     } while (recheck);
  437. }
  438.  
  439.  
  440. static int bsearch(s, dlist, n)
  441.     char *s;
  442.     struct direct *(dlist[]);
  443.     int n;
  444. {
  445.     int first, k, last, res;
  446.  
  447.     for(first = 0, last = n - 1;;) {
  448.         if (last < first)
  449.             return(-1);
  450.         k = (first + last) >> 1;
  451.         if ((res = strcmp(s, dlist[k]->d_name)) == 0)
  452.             return(k);
  453.         if (res < 0)
  454.             last = k - 1;
  455.         else
  456.             first = k + 1;
  457.     }
  458. }
  459.  
  460.  
  461. static char** breakcycles(reps, nreps, dot)
  462.     REP *reps;
  463.     int nreps;
  464.     struct direct *(dot[]);
  465. {
  466.     char **tempnames;
  467.     int tempno;
  468.     REP *cur, *pred;
  469.  
  470.     tempnames = (char **)malloc(nreps * sizeof(char *) + 1);
  471.     tempno = 0;
  472.     for (cur = reps; cur != NULL; cur = cur->nextrep) {
  473.         if (cur->status == FORGET)
  474.             continue;
  475.         for (pred = cur->dofirst;
  476.              pred != NULL && pred != cur;
  477.              pred = pred->dofirst)
  478.         {
  479.             if (pred->status != DO) {
  480.                 fputs("Strange error in cycle checking.\n", stderr);
  481.                 exit(1);
  482.             }
  483.         }
  484.         if (pred == cur)
  485.             if (cur->dofirst == cur)
  486.                 cur->status = FORGET;
  487.             else {
  488.                 pred = (REP *)malloc(sizeof(REP));
  489.                 tempnames[++tempno] =
  490.                     (char *)malloc(sizeof(tempprefix) + strlen(cur->repname));
  491.                 strcpy(tempnames[tempno], tempprefix);
  492.                 strcat(tempnames[tempno], cur->repname);
  493.                 pred->repname = cur->repname;
  494.                 pred->ftorep = -tempno;
  495.                 pred->dofirst = cur->dofirst;
  496.                 pred->mustdel = -1;
  497.                 pred->status = DO;
  498.                 pred->nextrep = cur->nextrep;
  499.                 cur->repname = tempnames[tempno];
  500.                 cur->dofirst = NULL;
  501.                 cur->nextrep = pred;
  502.             }
  503.     }
  504.     return(tempnames);
  505. }
  506.  
  507.  
  508. static dorenames(reps, dot, tempnames, verbose)
  509.     REP *reps;
  510.     struct direct *(dot[]);
  511.     char *(tempnames[]);
  512.     int verbose;
  513. {
  514.     REP *cur;
  515.     int skipped;
  516.     char *fromname;
  517.  
  518.     do {
  519.         skipped = 0;
  520.         for (cur = reps; cur != NULL; cur = cur->nextrep) {
  521.             if (cur->status == DO) {
  522.                 if (cur->dofirst != NULL && cur->dofirst->status != DONE)
  523.                     skipped = 1;
  524.                 else {
  525.                     fromname = (cur->ftorep < 0 ?
  526.                         tempnames[-(cur->ftorep)] :
  527.                         dot[cur->ftorep]->d_name);
  528.                     cur->status = DONE;
  529.                     if (rename(fromname, cur->repname)) {
  530.                         fputs("Strange. Can not rename '", stderr);
  531.                         fputs(fromname, stderr);
  532.                         fputs("' to '", stderr);
  533.                         fputs(cur->repname, stderr);
  534.                         fputs("'\n", stderr);
  535.                     }
  536.                     else if (verbose) {
  537.                         fputs(fromname, stdout);
  538.                         fputs(" -> ", stdout);
  539.                         fputs(cur->repname, stdout);
  540.                         if (cur->mustdel >= 0)
  541.                             fputs(" (*)", stderr);
  542.                         fputc('\n', stdout);
  543.                     }
  544.                 }
  545.             }
  546.         }
  547.     } while (skipped);
  548. }
  549.