+
+/* The RENAME command - expanded and improved in 8.0.212 April 2006 */
+
+static char * ren_sub[4] = { NULL,NULL,NULL,NULL }; /* For RENAME /REPLACE */
+
+int ren_list = 0; /* Default listing action for RENAME */
+int ren_coll = RENX_OVWR; /* Default collision action */
+
+int
+shorename() {
+ char * s;
+ switch (ren_coll) {
+ case RENX_FAIL: s = "fail"; break;
+ case RENX_OVWR: s = "overwrite"; break;
+ case RENX_SKIP: s = "proceed"; break;
+ }
+ printf(" rename collision: %s\n",s);
+ printf(" rename list: %s\n",showoff(ren_list));
+ return(1);
+}
+
+int
+setrename() { /* Parse SET RENAME options */
+ int x, y;
+ if ((x = cmkey(renamset,nrenamset,"","", xxstring)) < 0)
+ return(x);
+ switch (x) {
+ case REN_OVW: /* COLLISION */
+ if ((x = cmkey(r_collision,nr_collision,"","", xxstring)) < 0)
+ return(x);
+ if ((y = cmcfm()) < 0)
+ return(y);
+ ren_coll = x;
+ break;
+ case DEL_LIS: /* LIST */
+ return(seton(&ren_list));
+ }
+ return(success = 1);
+}
+
+/* Reverse a string - Assumes a single-byte character set */
+
+int
+gnirts(s1, s2, len) char * s1, * s2; int len; {
+ int n, m = 0;
+ if (!s1) /* Null source pointer, fail */
+ return(0);
+ n = (int) strlen(s1);
+ if (n > len-1) /* Source longer than dest, fail */
+ return(0);
+ s2[n--] = NUL; /* Deposit null byte at end of dest */
+ for (; n >= 0; n--) { /* Copy the rest backwards */
+ *s2++ = s1[n];
+ m++;
+ }
+ return(m);
+}
+
+/*
+ r e n a m e o n e
+
+ Worker function to rename one file for dorenam() (below).
+ old = name of file or directory to be renamed
+ new = new name (not required for /UPPER, /LOWER, and /REPLACE)
+ replacing = 1 if doing string replacement on the name
+ casing = 1 if converting name to lowercase, 2 if to uppercase
+ all = if doing case conversion on all names, not just monocase ones
+ converting = 1 if converting character sets
+ cset1 = character set to convert from (File Character Set index)
+ cset2 = character set to convert to (ditto, see ck?xla.h)
+ listing = 1 to show results of rename
+ nolist = 1 to be completely silent (don't even print error messages)
+ op = 1 means simulate, 2 means check for collision, 0 means rename
+ size = length of result buffer.
+ collision = action to take if destination file already exists:
+ 0 = fail
+ 1 = overwrite and succeed
+ 2 = skip and succeed
+ Returns:
+ 0: on failure to rename or when a forbidden collision would have occurred.
+ 1: on success (file was renamed or did not need to be renamed).
+ Note:
+ If this code is ever built on any platform that is not Unix, Windows,
+ VMS, or OS/2, this routine might need some adjustment.
+*/
+
+/* Opcodes for op... */
+#define REN_OP_SIM 1 /* Simulate */
+#define REN_OP_CHK 2 /* Check for collisions */
+
+static int
+renameone(old,new,
+ replacing,casing,all,converting,cset1,cset2,
+ listing,nolist,op,size,collision)
+ char * old, * new;
+ int replacing,casing,all,converting,cset1,cset2,
+ listing,nolist,op,size,collision;
+{
+ char buf[CKMAXPATH]; /* Temporary filename buffer */
+ char out[CKMAXPATH]; /* Buffer for new name */
+ char dir[CKMAXPATH]; /* Destination directory */
+ char pat[CKMAXPATH]; /* Path segment on old filename */
+
+ char * destdir; /* Destination directory, if any */
+ char * srcpath; /* Source path, if any */
+ int rc = 1, flag = 0, skip = 0; /* Control */
+ int honorcase = 0, replaced = 0;
+ int anchor = 0; /* 1 = beginning, 2 = end */
+ int occur = 0; /* Occurrence */
+ int minus = 0; /* Occurrence is negative */
+ int allbut = 0; /* Occurrence is "all but" */
+ int arg2isfile = 0; /* Arg2 ("new") is a filename */
+
+ debug(F110,"RENAMEONE old",old,0);
+ debug(F110,"RENAMEONE new",new,0);
+ debug(F110,"RENAMEONE ren_sub[0]",ren_sub[0],0);
+ debug(F110,"RENAMEONE ren_sub[1]",ren_sub[1],0);
+ debug(F110,"RENAMEONE ren_sub[2]",ren_sub[2],0);
+
+ if (op == REN_OP_SIM && !nolist) /* For convenience */
+ listing = 1;
+#ifndef NOSPL
+ honorcase = inpcas[cmdlvl]; /* Inherit SET CASE value */
+#else
+#ifdef UNIX
+ honorcase = 1;
+#else
+ honorcase = 0;
+#endif /* UNIX */
+#endif /* NOSPL */
+
+ if (!old) old = ""; /* In case of bad args */
+ if (!new) new = "";
+ if (!*old) return(success = 0);
+ ckstrncpy(out,new,CKMAXPATH); /* So we don't write into */
+ new = out; /* our argument... */
+ size = CKMAXPATH;
+
+ pat[0] = NUL; /* Assume no path in source file.. */
+ srcpath = pat;
+ {
+ int n; /* If the old name includes a path */
+ n = (int)strlen(old) - 1; /* put it in a separate place. */
+ for (; n >= 0; n--) { /* We are renaming the file only. */
+ if (ISDIRSEP(old[n])) {
+ ckstrncpy(pat,old,CKMAXPATH);
+ pat[n+1] = NUL;
+ old = old+n+1;
+ break;
+ }
+ }
+ }
+ debug(F110,"RENAMEONE old 2",old,0);
+ debug(F110,"RENAMEONE pat 2",pat,0);
+
+ dir[0] = NUL; /* Assume no destination directory */
+ destdir = dir;
+ if (*new) { /* If Arg2 given */
+ if (isdir(new)) { /* If it's a directory */
+ ckstrncpy(dir,new,CKMAXPATH); /* put it here */
+ } else { /* otherwise */
+ arg2isfile++; /* flag that it's a filename */
+ }
+ }
+ if (!casing && !replacing && !converting) {
+ if (!*new)
+ return(success = 0);
+ if (!arg2isfile) { /* Destination is a directory? */
+ if (!isdir(old)) { /* and source is not? */
+#ifndef VMS
+ int n, x = 0; /* Concatenate them */
+ if ((n = strlen(new)) > 0) /* so we can check for */
+ if (ISDIRSEP(new[n-1])) /* collisions. */
+ x++;
+ ckmakmsg(buf,size,new,x ? "" : "/", old, "");
+#else
+ ckmakmsg(buf,size,new, old, NULL, NULL);
+#endif /* VMS */
+ debug(F110,"RENAMEONE new new",new,0);
+ new = buf;
+ size = CKMAXPATH;
+ }
+ }
+ } else if (*new) { /* Directory to move file to */
+ int n, x = 0; /* after changing its name */
+ if (!isdir(new))
+ return(success = 0);
+#ifndef VMS
+ if ((n = strlen(new)) > 0)
+ if (ISDIRSEP(new[n-1]))
+ x++;
+ ckmakmsg(dir,CKMAXPATH,new,x ? "" : "/", "", "");
+#else
+ ckstrncpy(dir,new,CKMAXPATH);
+#endif /* VMS */
+ }
+
+#ifndef NOCSETS
+#ifndef NOUNICODE
+ if (converting) {
+ new = cvtstring(old,cset1,cset2);
+ }
+#endif /* NOUNICODE */
+#endif /* NOCSETS */
+
+ if (replacing) { /* Replacing strings */
+ int todo = 0;
+ int len0, len1, len2;
+ char c, *p, *s, *bp[3];
+
+ bp[0] = old; /* Original name */
+ bp[1] = ren_sub[0]; /* String to be replaced */
+ bp[2] = ren_sub[1]; /* What to replace it with */
+ if (!bp[2]) bp[2] = "";
+
+ len0 = (int)strlen(bp[0]); /* length of original filename */
+ len1 = (int)strlen(bp[1]); /* length of target substring */
+ len2 = (int)strlen(bp[2]); /* Length of replacement string */
+
+ if (ren_sub[2]) { /* Optional options */
+ p = ren_sub[2];
+ while ((c = *p++)) {
+ switch (c) {
+ case '^':
+ anchor = 1; occur = 0; minus = 0; allbut = 0; break;
+ case '$':
+ anchor = 2; occur = 0; minus = 0; allbut = 0; break;
+ case 'A': honorcase = 1; minus = 0; allbut = 0; break;
+ case 'a': honorcase = 0; minus = 0; allbut = 0; break;
+ case '-': minus = 1; break;
+ case '~': allbut = 1; break;
+ default:
+ if (isdigit(c)) {
+ occur = c - '0';
+ if (minus) occur = 0 - occur;
+ anchor = 0;
+ }
+ minus = 0;
+ }
+ }
+ }
+ if (anchor) { /* Anchored replacement... */
+ y = len0 - len1;
+ if (y > 0) {
+ int x;
+ switch (anchor) {
+ case 1: /* Anchored at beginning */
+ if (!ckstrcmp(bp[1],bp[0],len1,honorcase)) {
+ x = ckstrncpy(new,bp[2],size);
+ (VOID) ckstrncpy(new+x,bp[0]+len1,size-x);
+ replaced = 1;
+ }
+ break;
+ case 2: /* Anchored at end */
+ if (!ckstrcmp(bp[1],bp[0]+y,len1,honorcase)) {
+ x = ckstrncpy(new,bp[0],y+1);
+ (VOID) ckstrncpy(new+y,bp[2],size-x);
+ replaced = 1;
+ }
+ break;
+ }
+ }
+ if (!replaced) {
+ ckstrncpy(new,old,size); /* Keep old name */
+ replaced = 1;
+ }
+ } else { /* Replace all occurrences */
+ int j, n = 0; /* or a particular occurrence */
+ char c;
+ int x = 0;
+ char * s0 = NULL, * s1 = NULL, * s2 = NULL;
+ p = new; /* Pointer to new name */
+
+ if (occur < 0) { /* nth occurrence from the right */
+ occur = 0 - occur;
+ s0 = (char *)malloc(len0+1); /* Reverse original string */
+ if (s0) {
+ (VOID) gnirts(bp[0],s0,len0+1);
+ bp[0] = s0;
+ } else return(0);
+ s1 = (char *)malloc(len1+1); /* Reverse target string */
+ if (s1) {
+ (VOID) gnirts(bp[1],s1,len1+1);
+ bp[1] = s1;
+ } else return(0);
+ s2 = (char *)malloc(len2+1); /* Reverse replacement string */
+ if (s2) {
+ (VOID) gnirts(bp[2],s2,len2+1);
+ bp[2] = s2;
+ } else return(0);
+ debug(F111,"RENAMEONE s0",s0,len0);
+ debug(F111,"RENAMEONE s1",s1,len1);
+ debug(F111,"RENAMEONE s2",s2,len2);
+ }
+ s = bp[0]; /* Pointer to old name */
+ p = new; /* Pointer to new name */
+ j = len0 - len1 + 1; /* How much to scan */
+ while (j-- > 0) { /* For each character... */
+ if (!ckstrcmp(bp[1],s,len1,honorcase)) { /* Match? */
+ n++; /* Occurrence counter */
+ todo = (occur == 0) ||
+ (!allbut && n == occur) ||
+ (allbut && n != occur);
+ if (!todo) { /* Desired occurrence? */
+ size -= ckstrncpy(p,bp[1],size); /* No... */
+ p += len1; /* Copy target string */
+ s += len1; /* instead of replacement string */
+ continue;
+ }
+ if (len2) { /* If replacement string not empty */
+ size -= ckstrncpy(p,bp[2],size); /* Copy it */
+ p += len2;
+ }
+ s += len1; /* Advance source position */
+ } else { /* No match */
+ *p++ = *s++; /* just copy this character */
+ size--;
+ }
+ }
+ while ((*p++ = *s++)); /* Done copy the rest */
+ replaced = 1; /* Remember we changed the name */
+ if (s0) { /* Were we doing "all but"? */
+ debug(F110,"RENAMEONE new1",new,0);
+ x = (int)strlen(new); /* Unreverse the result */
+ if ((p = (char *)malloc(x+2))) {
+ (VOID) gnirts(new,p,x+2);
+ debug(F110,"RENAMEONE new2",new,0);
+ ckstrncpy(new,p,x+2);
+ free(p);
+ }
+ if (s0) free(s0); /* Free the temporary strings */
+ if (s1) free(s1);
+ if (s2) free(s2);
+ debug(F110,"RENAMEONE new3",new,0);
+ }
+ }
+ }
+ if (casing) { /* Changing case? */
+ char c, * t; /* See if mixed case. */
+ if (!replaced)
+ ckstrncpy(new,old,size); /* Copy old name to new name */
+ t = new;
+ while ((c = *t++)) {
+ if (islower(c)) flag |= 1; /* Have a lowercase letter */
+ else if (isupper(c)) flag |= 2; /* Have an uppercase letter */
+ if (flag == 3) break; /* Have a mixed-case name */
+ }
+ if (all || flag < 3) { /* Not skipping or not mixed case */
+ if (casing == 1 && flag != 1) /* Change case to lower */
+ (VOID) cklower(new);
+ else if (casing == 2 && flag != 2) /* Change case to upper */
+ (VOID) ckupper(new);
+ }
+ }
+ debug(F110,"XXX 1 new",new,0);
+ debug(F110,"XXX 1 old",old,0);
+ debug(F110,"XXX 1 srcpath",srcpath,0);
+ debug(F110,"XXX 1 destdir",destdir,0);
+
+ if (*destdir && !arg2isfile) { /* Moving without renaming */
+ ckstrncat(srcpath,old,CKMAXPATH);
+ old = srcpath;
+ new = destdir;
+ } else if (*destdir || *srcpath) { /* Were there any pathnames? */
+ char tmp[CKMAXPATH];
+ ckmakmsg(tmp,CKMAXPATH,srcpath,old,NULL,NULL);
+ ckstrncpy(old,tmp,CKMAXPATH);
+ if (*destdir) { /* Directory-to-move-to given? */
+ ckstrncat(destdir,new,CKMAXPATH);
+ new = destdir;
+ } else if (*srcpath && !arg2isfile) { /* Or was there a source path? */
+ ckstrncat(srcpath,new,CKMAXPATH);
+ new = srcpath;
+ }
+ }
+ debug(F110,"XXX 2",new,0);
+
+ skip = 0; /* Can we skip this one? */
+#ifdef COMMENT
+ if (casing && !replaced) {
+ skip = (((all == 0) && (flag == 3)) || (flag == casing)) ? 1 : 0;
+ if (!skip && destdir) skip = 0;
+ }
+#endif /* COMMENT */
+ if (!skip) {
+ if (!ckstrcmp(old,new,-1,1))
+ skip = 1;
+ }
+ if (op == 0 && !skip && (collision != RENX_OVWR)) {
+ if (zchki(new) > (CK_OFF_T)-1) { /* New file already exists? */
+ switch (collision) { /* Yes, take specified action */
+ case RENX_SKIP: /* Skip this one and proceed */
+ skip = 2;
+ break;
+ case RENX_FAIL: /* Or fail. */
+ skip = 3;
+ if (!listing && !nolist)
+ printf("?File already exists: %s\n",new);
+ }
+ }
+ }
+ debug(F110,"RENAMEONE new",new,0);
+ debug(F101,"RENAMEONE flag","",flag);
+ debug(F101,"RENAMEONE skip","",skip);
+ debug(F100,"RENAMEONE ----------------","",0);
+
+ if (skip == 3) {
+ if (listing) printf("%s => %s (SKIPPED: %s already exists)\n",
+ old,new,new);
+ rc = 0;
+ } else if (skip) { /* Skipping this one */
+ if (listing) printf("%s => %s (%s)\n",
+ old,new,
+ (skip == 2) ? "COLLISION: SKIPPED" : "SKIPPED");
+ } else { /* Have to rename this one */
+ if (op == REN_OP_CHK) { /* Checking for collisions */
+ return((zchki(new) > (CK_OFF_T)-1) ? 0 : 1 );
+ } else if (op == REN_OP_SIM) { /* Simulating */
+ if (listing) printf("%s => %s (SIMULATED)\n",old,new);
+ } else { /* Really renaming */
+ if (listing) printf("%s => %s ",old,new);
+ if (zrename(old,new) < 0) {
+ rc = 0;
+ if (listing)
+ printf("(FAILED: %s)\n",ck_errstr());
+ else if (!nolist)
+ printf("?%s\n",ck_errstr());
+ } else {
+ if (listing) printf("(OK)\n");
+ }
+ }
+ }
+ return(success = rc); /* Succeeds also if nothing needed to be renamed */
+}
+