"Fossies" - the Fresh Open Source Software Archive

Member "rename-1.3/rename.c" (20 May 2002, 13601 Bytes) of package /linux/privat/old/rename-1.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2     rename.c -- file rename tool
    3 
    4     Copyright 1998,1999  Xuming <xuming@bigfoot.com>
    5 
    6     This program is free software; you can redistribute it and/or
    7     modify it under the terms of the GNU General Public License as
    8     published by the Free Software Foundation; either version 2, or
    9     (at your option) any later version.
   10 
   11     This program is distributed in the hope that it will be useful,
   12     but WITHOUT ANY WARRANTY; without even the implied warranty of
   13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14     General Public License, the file COPYING in this directory, for
   15     more details.
   16 
   17     You should have received a copy of the GNU General Public License
   18     along with this program; if not, write to the Free Software
   19     Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
   20 */
   21 
   22 #ifdef HAVE_CONFIG_H
   23 #include "config.h"
   24 #endif
   25 
   26 #include <ctype.h>
   27 #include <pwd.h>
   28 #include <stdio.h>
   29 #include <stdlib.h>
   30 #include <signal.h>
   31 #include <termios.h>
   32 #include <sys/stat.h>
   33 
   34 #if HAVE_UNISTD_H
   35   #include <sys/types.h>
   36   #include <unistd.h>
   37 #endif
   38 
   39 #if STDC_HEADERS
   40   #include <string.h>
   41 #else
   42   #ifndef HAVE_STRCHR
   43     #define strchr index
   44     #define strrchr rindex
   45   #endif
   46   char *strchr(), *strrchr();
   47 #endif
   48 
   49 #if HAVE_DIRENT_H
   50   #include <dirent.h>
   51   #define NAMLEN(dirent) strlen((dirent)->d_name)
   52 #else
   53   #define dirent direct
   54   #define NAMLEN(dirent) (dirent)->d_namlen
   55   #if HAVE_SYS_NDIR_H
   56     #include <sys/ndir.h>
   57   #endif
   58   #if HAVE_SYS_DIR_H
   59     #include <sys/dir.h>
   60   #endif
   61   #if HAVE_NDIR_H
   62     #include <ndir.h>
   63   #endif
   64 #endif
   65 
   66 #if HAVE_REGEX_H
   67   #include <regex.h>
   68 #elif
   69   #include "regex.h"
   70 #endif
   71 
   72 #if HAVE_GETOPT_H
   73   #include <getopt.h>
   74   #ifndef HAVE_GETOPT_LONG
   75     #error No getopt_long function
   76   #endif
   77 #else
   78   #error No getopt functions
   79 #endif
   80   
   81 #include "rename.h"
   82   
   83 int     prompt = 0; /* 0: ask; 1, No to all; other: Yes to all */
   84 int     oper = 0;   /* the operation: lowcase, upcase, substitute */
   85 int attr = 0;   /* some attributes of the operation */
   86 
   87 
   88 regex_t preg[1];    /* see manpage regex(7) */
   89 char    *patnbuf;
   90 char    *pattern;   /* memeory for storing fixed pattern */
   91 int     pnlen;
   92 char    *subst;
   93 int stlen;      /* length of substitute and pattern string */
   94 
   95 struct passwd *pwd = NULL;
   96 
   97 char    cwd[SVRBUF];    /* current working directory */
   98 
   99 
  100 void recursive(char *path);
  101 int  change_name(char *oldname);
  102 int  match_regexpr(char *str, int n);
  103 int  match_pattern(char *str, int n);
  104 int  match_backward(char *str, int n);
  105 int  setpattern(char *arg);
  106 int  do_rename(char *old, char *new);
  107 void sigbreak(int sig);
  108 void usage(int mode);
  109 
  110 char* (*StrStr)(const char *, const char *);
  111 int   (*StrnCmp)(const char *, const char *, size_t);
  112 
  113 int main(int argc, char **argv)
  114 {
  115     int     recurs = 0, c;
  116     struct  stat    fs;
  117     char    *sopt = "hVtluRvs:o:";
  118     struct  option  lopt[] = {
  119     { "lowcase",    0, NULL, 'l' },
  120     { "upcase",     0, NULL, 'u' },
  121     { "recursive",  0, NULL, 'R' },
  122     { "owner",      1, NULL, 'o' },
  123     { "verbose",    0, NULL, 'v' },
  124     { "test",   0, NULL, 't' },
  125     { "help",   0, NULL, 'h' },
  126     { "version",    0, NULL, 'V' },
  127     { "yes",    0, NULL, 2 },
  128     { "no",     0, NULL, 1 },
  129     { NULL, 0, NULL, 0 }
  130     };
  131 
  132     StrStr  = strstr;
  133     StrnCmp = strncmp;
  134     
  135     while ((c = getopt_long(argc, argv, sopt, lopt, NULL)) != EOF)  {
  136     switch (c) {
  137     case 'h':
  138         usage(0);
  139         return 0;
  140         
  141     case 'V':
  142         usage(1);
  143         return 0;
  144 
  145     case 1:
  146     case 2:
  147         prompt = c;
  148         break;
  149         
  150     case 'l':
  151         oper = ACT_LOWCASE;
  152         break;
  153         
  154     case 'u':
  155         oper = ACT_UPCASE;
  156         break;
  157         
  158     case 's':
  159         if (setpattern(optarg))  
  160         return -1;
  161         break;
  162         
  163     case 'o':
  164         if (geteuid() != 0)  {
  165         printf("Only super user can change file's owner!\n");
  166         return 1;
  167         }           
  168         pwd = getpwnam(optarg);
  169         if (pwd == NULL) {
  170         printf("No this user in current system [%s]\n", optarg);
  171         return 1;
  172         } else
  173         attr |= MOD_OWNER;
  174         break;
  175         
  176     case 't':
  177         attr |= MOD_VERBO | MOD_TEST;
  178         break;
  179         
  180     case 'v':
  181         attr |= MOD_VERBO;
  182         break;
  183         
  184     case 'R':
  185         recurs = 1;
  186         break;
  187     }
  188     }
  189     
  190     if ((oper == ACT_DEFT) && (attr & MOD_OWNER))
  191     oper = ACT_OWNER;
  192     
  193     if (oper == ACT_DEFT)  {
  194         
  195     /* just simplely change one filename to another */
  196 
  197     if ((optind + 2) > argc)  {
  198         printf("%s: missing file arguments.\n", argv[0]);
  199         return 1;
  200     } else if ((optind + 2) < argc)  {  
  201         printf("%s: too many arguments.\n", argv[0]);
  202         return 2;
  203     } else if (strcmp(argv[optind], argv[optind+1])) 
  204         do_rename(argv[optind], argv[optind+1]);
  205     
  206     return 0;
  207     }
  208 
  209     if (optind >= argc)  {
  210     printf("%s: missing file arguments.\n", argv[0]);
  211     return 1;
  212     }
  213     
  214     if (getcwd(cwd, SVRBUF) == NULL)  {
  215     printf("Out of path!\n");
  216     return -1;
  217     }
  218     
  219     signal(SIGINT, sigbreak);
  220     signal(SIGHUP, sigbreak);
  221     signal(SIGQUIT, sigbreak);
  222     signal(SIGTERM, sigbreak);
  223 
  224     for ( ; optind < argc; optind++)  {
  225 
  226     if (recurs)  {
  227         if (lstat(argv[optind], &fs) < 0)  {
  228         perror("lstat");
  229         continue;
  230         }
  231         if (S_ISDIR(fs.st_mode))  {
  232         recursive(argv[optind]);
  233         chdir(cwd);
  234         }
  235     }
  236     change_name(argv[optind]);
  237     }
  238     
  239     if (oper == ACT_REG)  
  240     regfree(preg);
  241     
  242 /*    chdir(cwd);*/
  243     return 0;
  244 }
  245 
  246 
  247 void recursive(char *path)
  248 {
  249     DIR     *dir;
  250     struct  stat    fs;
  251     struct  dirent  *de;
  252 
  253     if (attr & MOD_VERBO)  
  254     printf("Entering directory [%s]\n", path);
  255     
  256     if (chdir(path) < 0)  {
  257     perror("chdir");
  258     return;
  259     }
  260     
  261     if ((dir = opendir(".")) == NULL)  {
  262     perror("opendir");
  263     return;
  264     }
  265     while((de = readdir(dir)) != NULL)  {
  266         
  267         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))  
  268         continue;
  269         if (lstat(de->d_name, &fs) < 0)  
  270         continue;   /* maybe permission denied */
  271     
  272         if (S_ISDIR(fs.st_mode))  
  273         recursive(de->d_name);
  274         change_name(de->d_name);
  275     }
  276     closedir(dir);
  277     
  278     chdir("..");
  279     if (attr & MOD_VERBO)  
  280     printf("Leaving directory [%s]\n", path);
  281 }
  282 
  283 
  284 /* according to the defined ruler, this routine spawns a new filename 
  285  * and changes old one to this one. */
  286 
  287 int change_name(char *oldname)
  288 {
  289     char    *p, new[SVRBUF];
  290     int     n;
  291     
  292     strncpy(new, oldname, SVRBUF);
  293     new[SVRBUF-1]='\0';
  294 
  295     /* index the actual filename, not the full path */
  296     
  297     if ((p = strrchr(new, '/')) == NULL)  
  298     p = new;  
  299     else  
  300     p++;
  301     
  302     if (!strcmp(p, ".") || !strcmp(p, ".."))  
  303     return 1;
  304     
  305     n = new + SVRBUF - p;
  306     
  307     switch (oper)  {
  308     case ACT_LOWCASE:
  309         while (*p)  {
  310         *p = tolower(*p);
  311             p++;
  312         }
  313     break;
  314 
  315     case ACT_UPCASE:
  316         while (*p)  {
  317             *p = toupper(*p);
  318             p++;
  319         }
  320     break;
  321 
  322     case ACT_SUFFIX:
  323     n -= strlen(p) - pnlen;
  324     p += strlen(p) - pnlen;
  325     if (!StrnCmp(p, pattern, pnlen))
  326     {
  327         strncpy(p, subst, n);
  328         p[n-1]='\0';
  329     }
  330     break;
  331     
  332     case ACT_REG:
  333         match_regexpr(p, n);
  334     break;
  335     
  336     case ACT_SUBT:
  337     match_pattern(p, n);
  338     break;
  339 
  340     case ACT_BACKWD:
  341     match_backward(p, n);
  342     break;
  343 
  344     case ACT_OWNER:
  345     if (attr & MOD_TEST)
  346         break;
  347     if (chown(new, pwd->pw_uid, pwd->pw_gid) < 0)
  348         perror("chown");
  349     break;
  350     }
  351     
  352     if (!strcmp(oldname, new))  
  353     return 1;
  354     
  355     return do_rename(oldname, new);
  356 }
  357 
  358 
  359 /* to match a null-terminated string against the precompiled pattern buffer.
  360    When successed, it substitutes matches with the second parameter so
  361    the original string with enough buffer will be modified.
  362    Note: precompiled pattern buffer be set to globel.
  363    If no matches in the string, return 0.
  364 */
  365 
  366 int  match_regexpr(char *str, int n)
  367 {
  368     int     rs = 0;
  369     char    tmp[SVRBUF];
  370     regmatch_t  pmatch[1];
  371 
  372     while (!regexec(preg, str, 1, pmatch, 0))  {
  373     strncpy(tmp, str + pmatch->rm_eo, SVRBUF);
  374     tmp[SVRBUF-1]='\0';
  375     strncpy(str + pmatch->rm_so, subst, n - pmatch->rm_so);
  376     str[pmatch->rm_so+(n-pmatch->rm_so)-1]='\0';
  377     n -= strlen(str);
  378     str += strlen(str);
  379     strncat(str, tmp, n);
  380     rs++;
  381     if ((attr & MOD_REPT) == 0)  
  382         break;
  383     }
  384     return rs;
  385 }
  386 
  387 
  388 int  match_pattern(char *str, int n)
  389 {
  390     char  tmp[SVRBUF], *p;
  391     int   rs = 0;
  392 
  393     while ((p = StrStr(str, pattern)) != NULL)  {
  394     n -= p - str;
  395     strncpy(tmp, p + pnlen, SVRBUF);
  396     tmp[SVRBUF-1]='\0';
  397     strncpy(p, subst, n);
  398     if (n) p[n-1]='\0';
  399 
  400     p += stlen;
  401     n -= stlen;
  402     strncpy(p, tmp, n);
  403     if (n) p[n-1]='\0';
  404 
  405     str = p;
  406 
  407     rs++;
  408     if ((attr & MOD_REPT) == 0)  
  409         break;
  410     }
  411     return rs;
  412 }
  413 
  414 
  415 int match_backward(char *str, int n)
  416 {
  417     char  tmp[SVRBUF], *p;
  418     int   rs, room, l;
  419 
  420     /* deal some special situation */
  421     
  422     rs = strlen(str) - pnlen;
  423     if (rs < 0)  
  424     return 0;   /* the pattern is longer than dest. string */
  425     
  426     if (rs == 0)  
  427     {
  428     if (StrStr(str, pattern))  
  429     {
  430         l = (stlen < n) ? stlen : n ;
  431         strncpy(str, subst, l);
  432         if ( l ) str[l-1]='\0';
  433         return 1;
  434     }
  435     return 0;
  436     }
  437     
  438     room = n - rs;
  439     for (p = str + rs, rs = 0; p >= str; p--, room++)  {
  440         
  441     if (!StrnCmp(p, pattern, pnlen))  
  442     {   
  443         strncpy(tmp, p + pnlen, SVRBUF);
  444         tmp[SVRBUF-1]='\0';
  445         strncpy(p, subst, room);
  446         if ( room ) p[room-1]='\0';
  447         l = room - stlen;
  448         strncpy(p + stlen, tmp, l);
  449         if ( l ) p[stlen+l-1]='\0';
  450         rs++;
  451         if ((attr & MOD_REPT) == 0)  
  452         break;
  453     }
  454     }
  455     return rs;
  456 }    
  457     
  458 
  459 int setpattern(char *arg)
  460 {
  461     char *idx[4], *p; 
  462     int  cflags = 0;
  463 
  464     patnbuf = dup_str(arg);
  465     fixtoken(patnbuf, idx, 4, "/");
  466     
  467     pattern = idx[1];
  468     if (pattern == NULL)  
  469     return -1;
  470     else 
  471     pnlen = strlen(pattern);
  472     
  473     subst = idx[2];
  474     if (subst == NULL)
  475     return -1;
  476     else
  477     stlen = strlen(subst);
  478     
  479     oper = ACT_SUBT;
  480     for (p = idx[3]; p && *p; p++)  {
  481     switch (*p)  {
  482     case 'g':
  483     case 'G':
  484         attr |= MOD_REPT;
  485         break;
  486     
  487     case 'b':
  488     case 'B':
  489         oper = ACT_BACKWD;
  490         break;
  491 
  492     case 's':
  493     case 'S':
  494         oper = ACT_SUFFIX;
  495         break;
  496 
  497     case 'i':
  498     case 'I':
  499         attr |= MOD_ICASE;
  500         StrnCmp = strncasecmp;
  501         StrStr = strcasestr;
  502         cflags |= REG_ICASE;
  503         break;
  504     
  505     case 'r':
  506     case 'R':
  507         oper = ACT_REG;
  508         break;
  509 
  510     case 'e':
  511     case 'E':
  512         oper = ACT_REG;
  513         cflags |= REG_EXTENDED;
  514         break;
  515     }
  516     }
  517 
  518     if (oper == ACT_REG)  {
  519     if (regcomp(preg, pattern, cflags))  {
  520         printf("Compiling regular expression failed. [%s]\n", pattern);
  521         return -2;
  522     }
  523     }
  524     
  525 /*    if (attr & MOD_TEST) 
  526     printf("pattern: %s subst: %s refs: %s\n", pattern, subst, idx[2]);*/
  527     
  528     return 0;
  529 }
  530 
  531 
  532 /* returns: 0 rename ok, skip or test. others means work failed */
  533 
  534 int do_rename(char *oldp, char *newp)
  535 {
  536     struct stat fs;
  537     char *p, *new, buf[64];
  538     int  i, rs = 0;
  539 
  540     if (!stat(newp, &fs) && S_ISDIR(fs.st_mode))  {
  541     /* the destination is directory, which means we must move the 
  542      * original file into this directory, just like mv(1) does */
  543     new = alloca(strlen(oldp) + strlen(newp) + 5);
  544     strcpy(new, newp);
  545     for (i = strlen(new) - 1; i >= 0; i--) {
  546         if (new[i] != '/')
  547         break;
  548     }
  549     new[i+1] = '/';
  550     new[i+2] = '\0';
  551     strcat(new, oldp);
  552     } else {
  553     new = alloca(strlen(newp) + 1);
  554     strcpy(new, newp);
  555     }   
  556     
  557     if (attr & MOD_VERBO)  
  558     printf("rename %-20s => %-20s : ", oldp, new);
  559     
  560     if (!stat(new, &fs))  {
  561     /* the target file has existed already */
  562 
  563     if (prompt == 1)  {
  564         goto skip;
  565         
  566     } else if (prompt == 0)  {
  567         fprintf(stderr, "overwrite %s? (Yes, No, All, nO_to_all) ", new);
  568         tcflush(0, TCIFLUSH);
  569         read(0, buf, 64);
  570         p = skip_space(buf);
  571         
  572         switch (*p)  {
  573         case 'a':
  574         case 'A':
  575         prompt = 2;
  576 
  577         case 'y':
  578         case 'Y':
  579         break;
  580             
  581         case 'o':
  582         case 'O':
  583         prompt = 1;
  584         
  585         case 'n':
  586         case 'N':
  587         default:
  588         goto skip;
  589         }
  590     }
  591     }
  592     
  593     if (attr & MOD_TEST) {
  594     if (attr & MOD_VERBO)
  595         printf("tested\n");
  596     } else {
  597     rs = rename(oldp, new);
  598     if (rs < 0) 
  599         perror("rename");
  600     else if (attr & MOD_VERBO)
  601         printf("ok\n");
  602     
  603     if (attr & MOD_OWNER)
  604         chown(new, pwd->pw_uid, pwd->pw_gid);
  605     }
  606 
  607     return rs;
  608 
  609 skip:
  610     if (attr & MOD_VERBO) 
  611     printf("skiped\n");
  612     return rs;
  613 }
  614 
  615     
  616 void sigbreak(int sig)
  617 {
  618 
  619     if (oper == ACT_REG)  
  620     regfree(preg);
  621     
  622     chdir(cwd);
  623     exit(sig);
  624 }
  625     
  626     
  627 void usage(int mode)
  628 {
  629 
  630     char *help = "\
  631 Usage: rename SOURCE DEST\n\
  632    or: rename [OPTION] file ...\n\
  633 Rename SOURCE to DEST, or substitute characters match the specified pattern\n\
  634 in the filename.\n\
  635 \n\
  636   -l, --lowcase            lowcase the file names\n\
  637   -u, --upcase             upcase the file names\n\
  638   -s/PATTERN/STRING[/sw]   replace matching PATTERN with STRING, [sw] is\n\
  639                            [g] replace all occurrences in the filename\n\
  640                            [i] ignore case when searching\n\
  641                            [b] backward searching and replacing\n\
  642                            [s] change file's suffix name\n\
  643                            [r] PATTERN is regular expression\n\
  644                            [e] PATTERN is extended regular expression\n\
  645   -R, --recursive          operate on files and directories recursively\n\
  646   -o, --owner  OWNER       change file's owner (superuser only)\n\
  647   -v, --verbose            display verbose information\n\
  648   -t, --test               test only\n\
  649   -h, --help               display this help and exit\n\
  650   -V, --version            output version information and exit\n\
  651       --yes  --no          force to choose YES or NO when target exists\n\
  652 \n\
  653 See man page regex(7) for detail information about extended regular expression.\n\
  654 \n\
  655 Report bugs to <xuming@bigfoot.com>.\n";
  656 
  657     char *version = "rename tool version %s\n";
  658 
  659     if (mode)  
  660     printf(version, VERSION);  
  661     else  
  662     printf(help);
  663 }
  664