home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / me34src.zip / me3 / util / canonize.c < prev    next >
C/C++ Source or Header  |  1995-01-14  |  8KB  |  241 lines

  1. /*
  2.  * Name: canonize
  3.  * Purpose:
  4.  *  Calculate a file's unique (canonical) pathname, suitable for comparing
  5.  *    with other canonical names.
  6.  * Usage: char *canonize(fname,cname, current_directory);
  7.  * Input:
  8.  *   char *fname;  Pointer to file name to canonize.
  9.  *   char *cname;  Where to put the canonical name.
  10.  *   char *current_directory;
  11.  *            Pointer to the name of the current directory
  12.  *           (canonized).  Pass in (char *)NULL if you want me to
  13.  *           query the OS for this info (the normal case).
  14.  * Returns:
  15.  *   Pointer to canonical name, or NULL if file name was illegal.
  16.  * Notes:
  17.  *   On Unix, getcwd() is VERY slow.  If you call canonize() very often,
  18.  *     you'll want to call getcwd() once, save the dir and pass it in via
  19.  *     current_directory.  Make sure current_directory is canonized.
  20.  *   No wildcards in fname - see fxpand().
  21.  *   MS-DOS
  22.  *     "A:" => "A:<current directory>"
  23.  *     The canonical name will only contain "/"s.  No "\"s.
  24.  *     Will parse both "\" and "/".
  25.  *     The drive letter is uppercase, everything else is lowercase.
  26.  *   ATARIST (from jwahar r.  bammi (bammi@cadence.com))
  27.  *     Usage just like Unix.  The library getcwd will canonize, and will be
  28.  *     consistent (especially) inspite of unixmode.  Also getcwd() is not
  29.  *     expensive on the atari, so we use it.
  30.  *   Every file has exactly one, unique canonical name.  Canonical names may
  31.  *     be compared to determine if two names specify the same file.
  32.  *   The format for canonical names is:  C:/path/FILE.EXT or C:/path/FILE
  33.  *     for files lacking an extension.  Note that the drive name is
  34.  *     always included, a complete path name (starting at the root) is
  35.  *     provided and references to "."  and ".."  are removed.
  36.  *   Leading blanks in file name are ignored and dropped from the
  37.  *     canonical name.
  38.  *   On Domain/OS (Apollo), 2 leading slashes (//foo) is very different
  39.  *     from a single leading slash.  Yech.  "Normal" Unix doesn't care if
  40.  *     there is one or more leading slashes.  Also, "//foo/.."  => "//".
  41.  *     "/.."  => "//" but I don't know if I care enough to make this
  42.  *     work.
  43.  * Defects:
  44.  *   Character device names are not recognized, and so they are treated
  45.  *     like ordinary files.
  46.  *   Error checking is not very robust.
  47.  * WARNING
  48.  *   This routine is a classic bomb waiting to go off.  It fills a fixed
  49.  *     length string (of unknown length) with text of unknown lengths.  One
  50.  *     of these days it will write off the end.
  51.  *   Also, the routine getcwd() is used poorly.  I should do getter error
  52.  *     checking and give it a better chance of working with a long path name.
  53.  *   The real fix for this is to use dynamic strings.  Until then, pass in a
  54.  *     big buffer.
  55.  * System:
  56.  *   MS-DOS 2.1 and up. Lattice C
  57.  *   With minor mods:  Microsoft C
  58.  *   Unix
  59.  *   Domain/OS (mostly)
  60.  * Author:    Jim Conrad    06/08/86
  61.  * Rewritten:    Craig Durland    9/86, 6/89
  62.  * Modified:  11/91
  63.  */
  64.  
  65. /* Copyright 1986 Craig Durland
  66.  *   Distributed under the terms of the GNU General Public License.
  67.  *   Distributed "as is", without warranties of any kind, but comments,
  68.  *     suggestions and bug reports are welcome.
  69.  */
  70.  
  71. #include <stdio.h>
  72. #include <os.h>
  73. #include <const.h>
  74. #include <char.h>    /* <char.h> or <ctype.h> */
  75.  
  76. extern char *getcwd();
  77.  
  78. static char *fnptr, *cnptr;        /* !!! not reentrent */
  79. static int get_dpart();
  80.  
  81.     /* flags for get_dpart() */
  82. #define TEXT    0
  83. #define PARENT    1
  84. #define DONE    2
  85. #define ZILCH    3
  86.  
  87. #if MSDOZ
  88.  
  89. #include <dos.h>
  90.  
  91. #ifdef LATTICE        /* from dos.h */
  92.  
  93. #define GET_DRIVE()                getdsk()
  94. #define GET_CURRENT_DIRECTORY(drive, path)    getcd(drive, path)
  95.  
  96. #else        /* JPI Topspeed C, Turbo C, hopefully others */
  97.  
  98. #include <dir.h>
  99.  
  100. #define GET_DRIVE()                getdisk()
  101. #define GET_CURRENT_DIRECTORY(drive, path)    getcurdir(drive, path)
  102.  
  103. #endif /* LATTICE */
  104. #endif /* MSDOZ */
  105.  
  106.         /* convert fname to a full file spec */
  107. char *canonize(fname,cname, current_directory)
  108.   char *fname; char *cname, *current_directory;
  109. {
  110.   register char *first_slash;
  111.  
  112.   fnptr = fname; cnptr = cname;
  113.  
  114.   while (isspace(*fnptr)) fnptr++;    /* Ignore leading white space */
  115.  
  116.     /* Ensure file name is not empty ie contains at least "c\0" */
  117.   if (*fnptr == '\0') return NULL;
  118.  
  119. #if MSDOZ
  120.   if (fnptr[1] == ':')        /* if drive provided, use it */
  121.     { *cnptr++ = toupper(*fnptr); fnptr += 2; }
  122.   else *cnptr++ = 'A' + GET_DRIVE();    /* drive missing: use current drive */
  123.   *cnptr++ = ':';
  124. #endif    /* MSDOZ */
  125.  
  126.   first_slash = cnptr;                /* point to opening slash */
  127.   if (ISSLASH(*fnptr))
  128.   {
  129.     *cnptr++ = U_SLASH;
  130. #if DOMAIN_OS        /* just for Domain/OS.  Yech */
  131.     {
  132.       char c;
  133.       /* Check to see if fname starts with // (and only //) */
  134.       if (ISSLASH(fnptr[1]))
  135.     if ((c = fnptr[2]) == '\0' || !ISSLASH(c))
  136.       { fnptr++; *cnptr++ = U_SLASH; first_slash++; }
  137.     }
  138. #endif    /* DOMAIN_OS */
  139.   }
  140.   else            /* No leading "/" => path name not anchored */
  141.   {            /* so fname is relative to current directory */
  142. #if MSDOZ
  143.     *cnptr++ = U_SLASH;            /* tack on a leading slash */
  144.     if (GET_CURRENT_DIRECTORY(*cname - 'A' + 1, cnptr)) return NULL;
  145.     if (*cnptr)            /* if current dir != "" (ie the root) */
  146.     {        /* skip over dir, switching slashes as we go along */
  147.       for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; 
  148.       *cnptr++ = U_SLASH;
  149.     }
  150. #else
  151.  
  152. #if UX_OS
  153.     if (current_directory)        /* better be canonized */
  154.     strcpy(cnptr,current_directory);
  155.     else if (getcwd(cnptr,250) == NULL) return NULL;    /* at least a "/" */
  156.     while (*cnptr) cnptr++;        /* move to end of current directory */
  157.     if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH;   /* in case cd is "/" */
  158. #else
  159.  
  160. #if ATARI
  161.     if (getcwd(cnptr,250) == NULL) return NULL;        /* at least a "/" */
  162. #if defined(__MINT__)     /* just in case */
  163.     for (; *cnptr; cnptr++) if (*cnptr == M_SLASH) *cnptr = U_SLASH; 
  164. #else
  165.     while (*cnptr) cnptr++;        /* move to end of current directory */
  166. #endif    /* __MINT__ */
  167.     if (cnptr[-1] != U_SLASH) *cnptr++ = U_SLASH;   /* in case cd is "/" */
  168. #else
  169.  
  170.     /* Nothing defined, nuke the compile */
  171.     Need_to_define = Something;
  172. #endif  /* ATARI */
  173. #endif  /* UX_OS */
  174. #endif    /* MSDOZ */
  175.   }
  176.  
  177.   while (TRUE)
  178.   {
  179.     switch (get_dpart())
  180.     {
  181.       case DONE:   *cnptr = '\0'; goto done;
  182.       case PARENT: 
  183.     if (--cnptr != first_slash) while (*--cnptr != U_SLASH) ;
  184.     /* now tack on a slash after the name */
  185.       case TEXT: *cnptr++ = U_SLASH; break;
  186.     }
  187.   }
  188. done:
  189.   if (--cnptr != first_slash) *cnptr = '\0';  /* get rid of trailing slash */
  190.  
  191. #if MSDOZ
  192.   lowercase(first_slash);    /* lowercase all but drive */
  193. #endif    /* MSDOZ */
  194.   return cname;
  195. }
  196.  
  197.     /* know: cnptr[-1] == slash */
  198. static int get_dpart()
  199. {
  200.   register char *ptr = cnptr;
  201.   register char c1, c2;
  202.  
  203.   if (*fnptr == '.')        /* check for /. or /.. */
  204.   {
  205.     fnptr++;            /* point to "/", "." or "" */
  206.     c1 = fnptr[0];
  207.     if (!(ISSLASH(c1) || c1 == '\0'))        /* ignore "/./" or "/." */
  208.     {
  209.       c2 = fnptr[1];
  210.       if (c1 == '.' && (ISSLASH(c2) || c2 == '\0'))    /* "/../" or "/.." */
  211.     { fnptr++; return PARENT; }
  212.       *cnptr++ = '.';                /* false alarm */
  213.     }
  214.   }
  215.   while (*fnptr)
  216.   {
  217.     if (ISSLASH(*fnptr)) { fnptr++; break; }
  218.     *cnptr++ = *fnptr++;
  219.   }
  220.   if (cnptr != ptr)   return TEXT;
  221.   if (*fnptr == '\0') return DONE;
  222.   return ZILCH;        /* probably something like multiple slashes */
  223. }
  224.  
  225.  
  226.  
  227. #ifdef TEST
  228. /* ******************************************************************** */
  229. /* *************** Test *********************************************** */
  230. /* ******************************************************************** */
  231.  
  232. main()
  233. {
  234.   char buf[80], cname[512];
  235.  
  236.   printf("File name to canonize: "); gets(buf);
  237.   if (NULL == canonize(buf,cname, (char *)NULL)) printf("Bleech\n");
  238.   else printf(">%s<\n>%s<\n",buf,cname);
  239. }
  240. #endif
  241.