static char rcsid[] = "$Id: canonize.c,v 1.3 1992/11/10 03:14:34 mike Exp $"; /* $Log: canonize.c,v $ * Revision 1.3 1992/11/10 03:14:34 mike * - `canonize' now works for path names, too. * * Revision 1.2 1992/09/07 00:33:52 mike * - Rewrote `canonize' completely for the ATARI version. * * Revision 1.1 1992/09/06 19:31:32 mike * Initial revision * */ /* * Name: canonize * Purpose: * Calculate a file's unique (canonical) pathname, suitable for comparing * with other canonical names. * Usage: char *canonize(fname,cname, curdir); * Input: * char *fname; Pointer to file name to canonize. * char *cname; Where to put the canonical name. * char *curdir; Pointer to the name of the current directory. Pass in * (char *)NULL if you want me to query the OS for this * info (the normal case). * Returns: * Pointer to canonical name, or NULL if file name was illegal. * Notes: * On Unix, getcwd() is VERY slow. If you call canonize() very often, * you'll want to call getcwd() once, save the dir and pass it in via * curdir. * No wildcards in fname - see fxpand(). * MS-DOS * "A:" => "A:" * The canonical name will only contain "/"s. No "\"s. * Will parse both "\" and "/". * The drive letter is uppercase, everything else is lowercase. * ATARIST (from jwahar r. bammi (bammi@cadence.com)) * Usage just like Unix. The library getcwd will canonize, and will be * consistent (especially) inspite of unixmode. Also getcwd() is not * expensive on the atari, so we use it. * Every file has exactly one, unique canonical name. Canonical names may * be compared to determine if two names specify the same file. * The format for canonical names is: C:/path/FILE.EXT or C:/path/FILE * for files lacking an extension. Note that the drive name is * always included, a complete path name (starting at the root) is * provided and references to "." and ".." are removed. * Leading blanks in file name are ignored and dropped from the * canonical name. * On Domain/OS (Apollo), 2 leading slashes (//foo) is very different * from a single leading slash. Yech. "Normal" Unix doesn't care if * there is one or more leading slashes. Also, "//foo/.." => "//". * "/.." => "//" but I don't know if I care enough to make this * work. * Defects: * Character device names are not recognized, and so they are treated * like ordinary files. * Error checking is not very robust. * WARNING * This routine is a classic bomb waiting to go off. It fills a fixed * length string (of unknown length) with text of unknown lengths. One * of these days it will write off the end. * Also, the routine getcwd() is used poorly. I should do getter error * checking and give it a better chance of working with a long path name. * The real fix for this is to use dynamic strings. Until then, pass in a * big buffer. * System: * MS-DOS 2.1 and up. Lattice C * With minor mods: Microsoft C * Unix * Domain/OS (mostly) * Author: Jim Conrad 06/08/86 * Rewritten: Craig Durland 9/86, 6/89 * Modified: 11/91 */ /* Copyright 1986 Craig Durland * Distributed under the terms of the GNU General Public License. * Distributed "as is", without warranties of any kind, but comments, * suggestions and bug reports are welcome. */ #include #include #include #include /* or */ extern char *getcwd(); static char *fnptr, *cnptr; /* !!! not reentrent */ static int get_dpart(); /* flags for get_dpart() */ #define TEXT 0 #define PARENT 1 #define DONE 2 #define ZILCH 3 #ifdef ATARI /* * Strip off the last dir name of a path. * `p' points to the byte after the last valid * char in the path, not the slash. */ static char *strip_last_dir(p,cname) register char *p; register char *cname; { while (--p >= cname) if (*p == U_SLASH) return p + 1; return NULL; } char *canonize(fname,cname,curdir) char *fname; char *cname; char *curdir; { register char *cp; register char *fp; int prefix; cp = cname; fp = fname; /* * See if `fname' contains a path prefix. */ prefix = 0; if (fp[0] == M_SLASH || fp[0] == U_SLASH) { if (tolower(fp[1]) == 'd' && tolower(fp[2]) == 'e' && tolower(fp[3]) == 'v' && (fp[4] == M_SLASH || fp[4] == U_SLASH) && isalpha(fp[5]) && (fp[6] == M_SLASH || fp[6] == U_SLASH)) prefix = 1; else prefix = 2; } else if (fp[1] == ':') prefix = 3; else if (fp[0] == '.') { if (fp[1] == M_SLASH || fp[1] == U_SLASH || fp[1] == '\0') prefix = 4; else if (fp[1] == '.' && (fp[2] == M_SLASH || fp[2] == U_SLASH || fp[2] == '\0')) prefix = 5; } switch (prefix) { case 0 : /* * No path prefix. */ if (curdir == NULL) { if (getcwd(cp,250) == NULL) return NULL; } else strcpy(cp,curdir); while (*cp++); --cp; if (cp[-1] != U_SLASH) *cp++ = U_SLASH; strcpy(cp,fp); return cname; case 1 : /* * A path of the form /dev/x.... */ *cp++ = tolower(fp[5]); *cp++ = ':'; *cp++ = U_SLASH; fp += 7; break; case 2 : /* * A path starting with a slash. */ if (curdir == NULL) { if (getcwd(cp,250) == NULL) return NULL; } else strcpy(cp,curdir); cp += 3; fp++; break; case 3 : /* * A path starting with a drive id and a colon. */ *cp++ = tolower(*fp++); *cp++ = *fp++; *cp++ = U_SLASH; fp++; break; case 4 : /* * A path starting with a dot. */ if (curdir == NULL) { if (getcwd(cp,250) == NULL) return NULL; } else strcpy(cp,curdir); while (*cp++); --cp; if (fp[1] == M_SLASH || fp[1] == U_SLASH) { *cp++ = U_SLASH; fp += 2; } else fp++; break; case 5 : /* * A path starting with two dots. */ if (curdir == NULL) { if (getcwd(cp,250) == NULL) return NULL; } else strcpy(cp,curdir); while (*cp++); --cp; if ((cp = strip_last_dir(cp,cname)) == NULL) return NULL; if (fp[2] == M_SLASH || fp[2] == U_SLASH) fp += 3; else fp += 2; } /* * At this point we got the drive id, the colon and the * slash, `fp' points to the first directory in the remaining * path or to the filename. */ while (*fp) { switch (*fp) { case M_SLASH : case U_SLASH : *cp++ = U_SLASH; if (*fp++ == '.') { if (fp[1] = '.' && (fp[2] == M_SLASH || fp[2] == U_SLASH)) { if ((cp = strip_last_dir(cp - 1, cname)) == NULL) return NULL; fp += 3; } else if (fp[1] == M_SLASH || fp[1] == U_SLASH) fp += 2; } break; default : *cp++ = tolower(*fp++); } } *cp = '\0'; if (cp == cname) return NULL; return cname; } #else /* convert fname to a full file spec */ char *canonize(fname,cname, curdir) char *fname; char *cname, *curdir; { register char *first_slash; fnptr = fname; cnptr = cname; while (isspace(*fnptr)) fnptr++; /* Ignore leading white space */ /* Ensure file name is not empty ie contains at least "c\0" */ if (*fnptr == '\0') return NULL; #if MSDOZ if (fnptr[1] == ':') /* if drive provided, use it */ { *cnptr++ = toupper(*fnptr); fnptr += 2; } else *cnptr++ = 'A' +getdsk(); /* drive missing: use current drive */ *cnptr++ = ':'; #endif /* MSDOZ */ first_slash = cnptr; /* point to opening slash */ if (ISSLASH(*fnptr)) { *cnptr++ = U_SLASH; #if DOMAIN_OS /* just for Domain/OS. Yech */ { char c; /* Check to see if fname starts with // (and only //) */ if (ISSLASH(fnptr[1])) if ((c = fnptr[2]) == '\0' || !ISSLASH(c)) { fnptr++; *cnptr++ = U_SLASH; first_slash++; } } #endif /* DOMAIN_OS */ } else /* No leading "/" => path name not anchored */ { /* so fname is relative to current directory */ #if MSDOZ *cnptr++ = U_SLASH; /* tack on a leading slash */ if (getcd(*cname -'A' +1,cnptr)) return NULL; if (*cnptr) /* if current dir != "" (ie the root) */ { /* skip over dir, switching slashes as we go along */ for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; *cnptr++ = U_SLASH; } #else #if UX_OS if (curdir) strcpy(cnptr,curdir); /* better be canonized */ else if (getcwd(cnptr,250) == NULL) return NULL; /* at least a "/" */ while (*cnptr) cnptr++; /* move to end of current directory */ if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH; /* in case cd is "/" */ #else #if ATARI if (getcwd(cnptr,250) == NULL) return NULL; /* at least a "/" */ #if defined(__MINT__) /* just in case */ for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; #else while (*cnptr) cnptr++; /* move to end of current directory */ #endif /* __MINT__ */ if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH; /* in case cd is "/" */ #else /* Nothing defined, nuke the compile */ Need_to_define = Something; #endif /* ATARI */ #endif /* UX_OS */ #endif /* MSDOZ */ } while (TRUE) { switch (get_dpart()) { case DONE: *cnptr = '\0'; goto done; case PARENT: if (--cnptr != first_slash) while (*--cnptr != U_SLASH) ; /* now tack on a slash after the name */ case TEXT: *cnptr++ = U_SLASH; break; } } done: if (--cnptr != first_slash) *cnptr = '\0'; /* get rid of trailing slash */ #if MSDOZ lowercase(first_slash); /* lowercase all but drive */ #endif /* MSDOZ */ return cname; } /* know: cnptr[-1] == slash */ static int get_dpart() { register char *ptr = cnptr; register char c1, c2; if (*fnptr == '.') /* check for /. or /.. */ { fnptr++; /* point to "/", "." or "" */ c1 = fnptr[0]; if (!(ISSLASH(c1) || c1 == '\0')) /* ignore "/./" or "/." */ { c2 = fnptr[1]; if (c1 == '.' && (ISSLASH(c2) || c2 == '\0')) /* "/../" or "/.." */ { fnptr++; return PARENT; } *cnptr++ = '.'; /* false alarm */ } } while (*fnptr) { if (ISSLASH(*fnptr)) { fnptr++; break; } *cnptr++ = *fnptr++; } if (cnptr != ptr) return TEXT; if (*fnptr == '\0') return DONE; return ZILCH; /* probably something like multiple slashes */ } #endif /* ATARI */ #ifdef TEST /* ******************************************************************** */ /* *************** Test *********************************************** */ /* ******************************************************************** */ main() { char buf[80], cname[512]; printf("File name to canonize: "); gets(buf); if (NULL == canonize(buf,cname, (char *)NULL)) printf("Bleech\n"); else printf(">%s<\n>%s<\n",buf,cname); } #endif