home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c005 / 4.ddi / C / FLNORM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1986-08-05  |  13.4 KB  |  516 lines

  1. /**
  2. *
  3. * Name        flnorm -- Check and normalize a path name (convert to
  4. *              standard form)
  5. *
  6. * Synopsis    ercode = flnorm(pfile,pnorm,plast);
  7. *
  8. *        int  ercode      0 if okay, 1 if error.
  9. *        char *pfile      Path name to normalize
  10. *        char *pnorm      Pointer to character buffer to fill
  11. *                  with the normalized path name.  This
  12. *                  should be at least MAX_FLEN bytes long.
  13. *        int  *plast      Pointer to integer variable in which
  14. *                  to return the offset within *pnorm of
  15. *                  the first character of the last
  16. *                  portion of the normalized path name.
  17. *
  18. * Description    This function validates a path name and converts it to a
  19. *        standard universal form for comparison with other path
  20. *        names.    This includes
  21. *
  22. *            1) Detecting all illegal characters, including
  23. *               extra periods ('.'), slashes ('/'), backslashes
  24. *               ('\\'), etc.;
  25. *            2) Converting all letters to lower case;
  26. *            3) Converting all slashes to backslashes;
  27. *            4) Removing the trailing colon (':') from a possible
  28. *               device name;
  29. *            5) Adding the current disk drive name (if no disk
  30. *               drive is named);
  31. *            6) Adding the full path from the root directory
  32. *               ("\\") if it is absent;
  33. *            7) Resolving "." and "..";
  34. *            8) Truncating each portion of the path name to eight
  35. *               characters + dot + three characters; and
  36. *            9) Removing the dot from each portion of the path name
  37. *               where it is redundant.
  38. *
  39. *        This does not include
  40. *
  41. *            1) Checking the existence of any portion of the path;
  42. *               or
  43. *            2) Checking for the presence of a possible device
  44. *               (if a trailing colon is found).
  45. *
  46. *        Note that unless the path name already begins with a
  47. *        full path from the root directory ("\\"), the actual
  48. *        result will depend (in part) on the current directory on
  49. *        the relevant drive.
  50. *
  51. *        The value returned in *plast is the index in *pnorm of
  52. *        the last filename in the normalized path name.    For
  53. *        example, if the normalized path name is
  54. *
  55. *            c:\blaise\flnorm.c
  56. *
  57. *        then *plast will be 10 (the offset of the 'f').  If
  58. *        there is no last filename (i.e., the path name is the
  59. *        root directory on some drive), then *plast will be the
  60. *        offset of the trailing NUL ('\0').  For example, if the
  61. *        normalized path name is
  62. *
  63. *            d:\
  64. *
  65. *        then *plast will be 3.
  66. *
  67. *        If the path name is found to contain illegal characters
  68. *        or syntax or if the normalized version is too long, 1 is
  69. *        returned as the value of the function and the results in
  70. *        *pnorm and *plast are invalid.
  71. *
  72. *        This routine does not know whether disk drive names have
  73. *        been reassigned with the DOS ASSIGN command.  If the
  74. *        ASSIGN command has been used, it is possible for two
  75. *        path names to be normalized to different results using
  76. *        FLNORM but actually to pertain to the same disk file.
  77. *
  78. *        The calling function must allocate at least MAX_FLEN
  79. *        bytes for the returned full path name.
  80. *
  81. * Returns    ercode          0 if okay, 1 if error.
  82. *        *pnorm          Character buffer filled with the
  83. *                  normalized path name.
  84. *        *plast          The offset within *pnorm of the first
  85. *                  character of the last portion of the
  86. *                  normalized path name.
  87. *
  88. * Version    3.0 (C)Copyright Blaise Computing Inc.    1986
  89. *
  90. **/
  91.  
  92. #include <ctype.h>
  93. #include <string.h>
  94.  
  95. #include <bdirect.h>
  96. #include <bfile.h>
  97. #include <bstring.h>
  98.  
  99. #define  MAXNAME    (MAX_FLEN - 1)    /* Maximum length of result     */
  100.  
  101. static int norm(char *);          /* Internal functions (see      */
  102. static int dodot(char *);          /* below).              */
  103. static int isfc(char);
  104.  
  105. int flnorm(pfile,pnorm,plast)
  106. register char *pfile;
  107. char          *pnorm;
  108. int          *plast;
  109. {
  110.     int      result;
  111.     register int j;
  112.     int      drive;
  113.  
  114.     /* Macro to quit immediately (perhaps because of error).          */
  115.  
  116. #define  EXIT        {pnorm[j] = '\0'; return result;}
  117.  
  118.     /* Macro to add one character to pnorm.                  */
  119.  
  120. #define  ADDCHAR(c) {if (j >= MAXNAME) {EXIT} else pnorm[j++] = c;}
  121.  
  122.     /* Macro to add trailing NUL to pnorm.                  */
  123.  
  124. #define  ENDNORM    {pnorm[j] = '\0';}
  125.  
  126.     /* Macro to step to next character in pfile.              */
  127.  
  128. #define  NEXT        {pfile++;}
  129.  
  130.     result = 1;               /* In case of premature exit.   */
  131.     j       =
  132.     *plast = 0;
  133.  
  134.     if (strlen(pfile) <= 0)
  135.     EXIT
  136.  
  137.     if (isalpha(*pfile) && *(pfile+1) == ':')
  138.     {                      /* A disk drive was named.      */
  139.     drive = tolower((int) *pfile) - (int) 'a';
  140.     NEXT                  /* Step past disk drive.          */
  141.     NEXT
  142.     }
  143.     else
  144.     {
  145.     drive = drretdrv();          /* Infer current drive.          */
  146.     }
  147.     ADDCHAR((char) (drive + 'a'))
  148.     ADDCHAR(':')
  149.  
  150.     if (*pfile == '\\' || *pfile == '/')
  151.     {                      /* Name starts from root.       */
  152.     ADDCHAR('\\')
  153.     NEXT
  154.     }
  155.     else
  156.     {                      /* Get start of path from system*/
  157.     if (   drcurdir(drive + 1,pnorm)
  158.         || norm(pnorm + 2))
  159.         EXIT
  160.     *pnorm = tolower(*pnorm);
  161.     j = (int) strlen(pnorm);
  162.     if (pnorm[j-1] != '\\')
  163.         ADDCHAR('\\')
  164.     }
  165.  
  166.     while (*pfile != '\0')            /* Copy remainder of pfile      */
  167.     {                      /* to pnorm.              */
  168.     ADDCHAR(*pfile)
  169.     NEXT
  170.     }
  171.     ENDNORM
  172.  
  173.     if (   dodot(pnorm + 2)          /* Resolve dot and double dot.  */
  174.     || norm(pnorm + 2))          /* Remove extra chars, fold to  */
  175.                       /* lower case, etc.          */
  176.  
  177.     return 1;              /* Quit if error.           */
  178.  
  179.     for (*plast = (int) strlen(pnorm);
  180.      *plast > 3 && pnorm[(*plast)-1] != '\\';
  181.      (*plast)--)
  182.      ;
  183.  
  184.     result = 0;               /* Success.              */
  185.     EXIT
  186. }
  187.  
  188. #undef    EXIT
  189. #undef    ADDCHAR
  190. #undef    NEXT
  191.  
  192. /**
  193. *
  194. * Name        norm -- Check and normalize a path name (convert to
  195. *              standard form)
  196. *
  197. * Synopsis    ercode = norm(pfile);
  198. *
  199. *        int  ercode      0 if okay, 1 if error.
  200. *        char *pfile      Path name to normalize
  201. *
  202. * Description    This function validates a path name and partially
  203. *        normalizes it for comparison with other path names.
  204. *        This includes
  205. *
  206. *            1) Detecting all illegal characters, including
  207. *               extra periods ('.'), slashes ('/'), backslashes
  208. *               ('\\'), etc.;
  209. *            2) Converting all letters to lower case;
  210. *            3) Converting all slashes to backslashes;
  211. *            4) Removing the trailing colon (':') from a possible
  212. *               device name;
  213. *            5) Truncating each portion of the path to eight
  214. *               characters + dot + three characters; and
  215. *            6) Removing the dot from each portion of the path
  216. *               where it is redundant.
  217. *
  218. *        This does not include
  219. *
  220. *            1) Checking the existence of any portion of the path;
  221. *            2) Checking for the presence of a possible device
  222. *               (if a trailing colon is found);
  223. *            3) Adding the current disk drive name;
  224. *            4) Adding the full path from the root directory
  225. *               ("\\"); or
  226. *            5) Resolving "." and "..".
  227. *
  228. *        The path name is assumed NOT to specify a disk drive.
  229. *
  230. *        If the path name is found to contain illegal characters
  231. *        or syntax, 1 is returned as the value of the function
  232. *        and the results in *pfile are invalid.
  233. *
  234. *        The result is written back to *pfile without increasing
  235. *        the length of the string.
  236. *
  237. * Returns    ercode          0 if okay, 1 if error.
  238. *        *pfile          Character buffer filled with the
  239. *                  normalized path name.
  240. *
  241. **/
  242.  
  243. static int norm(pfile)
  244. register char *pfile;
  245. {
  246.     int size;
  247.     int result;
  248.  
  249.     register char *ptarget = pfile;
  250.  
  251.     /* Macro to quit immediately (perhaps because of error).          */
  252.  
  253. #define  EXIT        {*ptarget = '\0'; return result;}
  254.  
  255.     /* Macro to add one character to ptarget.                  */
  256.  
  257. #define  ADDCHAR(c) {*ptarget++ = c;}
  258.  
  259.     /* Macro to back up one character in ptarget.              */
  260.  
  261. #define  BACKCHAR   {ptarget--;}
  262.  
  263.     /* Macro to step to next character in source.              */
  264.  
  265. #define  NEXT        {pfile++;}
  266.  
  267.     result = 1;               /* In case of early exit.       */
  268.  
  269.     if (*pfile == '\\' || *pfile == '/')
  270.     {
  271.     ADDCHAR('\\')
  272.     NEXT
  273.     }
  274.  
  275.     while (*pfile != 0)
  276.     {
  277.     size = 0;
  278.     while (   *pfile != '.'       /* Step through filename,       */
  279.            && *pfile != '\\'      /* saving up to 8 characters.   */
  280.            && *pfile != '/'
  281.            && *pfile != ':'
  282.            && *pfile != '\0')
  283.     {
  284.         if (!isfc(*pfile))
  285.         EXIT
  286.         if (size < 8)
  287.         {
  288.         ADDCHAR(tolower(*pfile))
  289.         size++;
  290.         }
  291.         NEXT
  292.     }
  293.     if (size <= 0)
  294.         EXIT              /* No filename was found.       */
  295.  
  296.     if (*pfile == '.')
  297.     {                  /* There may be an extension.   */
  298.         ADDCHAR('.')
  299.         NEXT
  300.         size = 0;
  301.         while (   *pfile != '\\'  /* Save first 3 chars of        */
  302.            && *pfile != '/'   /* extension.                   */
  303.            && *pfile != ':'
  304.            && *pfile != '\0')
  305.         {
  306.         if (!isfc(*pfile))
  307.             EXIT
  308.         if (size < 3)
  309.         {
  310.             ADDCHAR(tolower(*pfile))
  311.             size++;
  312.         }
  313.         NEXT
  314.         }
  315.         if (size <= 0)
  316.         BACKCHAR          /* Drop unneeded period ('.').  */
  317.     }
  318.  
  319.     if (*pfile == '\\' || *pfile == '/')
  320.     {
  321.         NEXT
  322.         if (*pfile == '\0')
  323.         EXIT              /* Error:  trailing slash or    */
  324.                       /* backslash after filename.    */
  325.         ADDCHAR('\\')
  326.     }
  327.     else if (*pfile == ':')
  328.     {
  329.         NEXT
  330.         if (*pfile == '\0')
  331.         result = 0;          /* OK -- this may be a device.  */
  332.         EXIT
  333.     }
  334.     }
  335.  
  336.     result = 0;               /* Success.              */
  337.     EXIT
  338. }
  339.  
  340. #undef    EXIT
  341. #undef    ADDCHAR
  342. #undef    BACKCHAR
  343. #undef    NEXT
  344.  
  345. /**
  346. *
  347. * Name        dodot -- Resolve dot (".") and double dot ("..")
  348. *             in a path name.
  349. *
  350. * Synopsis    ercode = dodot(pfile);
  351. *
  352. *        int  ercode      0 if okay, 1 if error.
  353. *        char *pfile      Path name to modify
  354. *
  355. * Description    This function removes the special file names "." and
  356. *        ".." from a full path name (which begins from the root
  357. *        directory of a drive).    Each ".." removes the previous
  358. *        subdirectory from the path; each "." is just removed.
  359. *
  360. *        This function does not
  361. *
  362. *            1) Detect illegal characters, such as
  363. *               extra periods ('.'), slashes ('/'), backslashes
  364. *               ('\\'), etc.;
  365. *            2) Convert letters to lower case;
  366. *            3) Remove the trailing colon (':') from a possible
  367. *               device name;
  368. *            4) Truncate any portion of the path to eight
  369. *               characters + dot + three characters;
  370. *            5) Remove any redundant dot from a directory name
  371. *               in the path;
  372. *            6) Check the existence of any portion of the path;
  373. *            7) Check for the presence of a possible device
  374. *               (if a trailing colon is found);
  375. *            8) Add the current disk drive name; or
  376. *            9) Add the full path from the root directory ("\\").
  377. *
  378. *        The path name is assumed NOT to specify a disk drive.
  379. *
  380. *        The result is written back to *pfile without increasing
  381. *        the length of the string.
  382. *
  383. *        Slashes are converted to backslashes.
  384. *
  385. *        This function detects when too many double dots ("..")
  386. *        have consumed the entire path or when a dot begins an
  387. *        illegal filename.  If an error occurs then 1 is returned
  388. *        as the value of the function and the result in *pfile is
  389. *        invalid.
  390. *
  391. * Returns    ercode          0 if okay, 1 if error.
  392. *        *pfile          Character buffer filled with the
  393. *                  modified path name.
  394. *
  395. **/
  396.  
  397. static int dodot(pfile)
  398. register char *pfile;
  399. {
  400.     int result,start;
  401.     register char *pfrom = pfile;
  402.     register int  j;
  403.  
  404.     /* Macro to quit immediately (perhaps because of error).          */
  405.  
  406. #define  EXIT        {pfile[j] = '\0'; return result;}
  407.  
  408.     /* Macro to add one character to target.                  */
  409.  
  410. #define  ADDCHAR(c) {pfile[j++] = c;}
  411.  
  412.     /* Macro to back up one character in target.              */
  413.  
  414. #define  BACKCHAR   {j--;}
  415.  
  416.     /* Macro to step to next character in source.              */
  417.  
  418. #define  NEXT        {pfrom++;}
  419.  
  420.     result = 1;               /* In case of early exit.       */
  421.     j       = 0;
  422.  
  423.     if (*pfrom == '\\' || *pfrom == '/')
  424.     {
  425.     ADDCHAR('\\')
  426.     NEXT
  427.     }
  428.     start = j;
  429.  
  430.     while (*pfrom != '\0')
  431.     {
  432.     if (*pfrom == '.')
  433.     {
  434.         NEXT
  435.         if (*pfrom == '.')
  436.         {                  /* Double dot:              */
  437.         NEXT
  438.         do              /* Strip off last directory     */
  439.         {              /* name.                  */
  440.             BACKCHAR
  441.         } while (j > 0 && pfile[j-1] != '\\');
  442.         if (j < start)
  443.             EXIT          /* Error:  went too far.          */
  444.         }
  445.         else
  446.         {
  447.         /* Do nothing:    just discard the lone dot. */
  448.         }
  449.         if (*pfrom == '\\' || *pfrom == '/')
  450.         {
  451.         NEXT
  452.         }
  453.         else if (*pfrom == '\0')
  454.         {
  455.         /* Do nothing. */
  456.         }
  457.         else
  458.         EXIT              /* Only slash or backslash may  */
  459.                       /* follow one or two periods.   */
  460.     }
  461.     else
  462.     {                  /* Just copy to next slash or   */
  463.                       /* backslash.              */
  464.         while (   *pfrom != '\\'
  465.            && *pfrom != '/'
  466.            && *pfrom != '\0')
  467.         {
  468.         ADDCHAR(*pfrom)
  469.         NEXT
  470.         }
  471.         if (*pfrom == '\\' || *pfrom == '/')
  472.         {
  473.         ADDCHAR('\\')
  474.         NEXT
  475.         }
  476.     }
  477.     }
  478.  
  479.     if (j > start && pfile[j-1] == '\\')
  480.     BACKCHAR              /* Remove extra trailing          */
  481.                       /* backslash.              */
  482.  
  483.     result = 0;
  484.     EXIT
  485. }
  486.  
  487. #undef    EXIT
  488. #undef    ADDCHAR
  489. #undef    BACKCHAR
  490. #undef    NEXT
  491.  
  492. /**
  493. *
  494. * Name        isfc -- Return whether a character is a valid DOS
  495. *            filename character.
  496. *
  497. * Synopsis    isokay = isfc(ch);
  498. *
  499. *        int  isokay      1 if okay, 0 if illegal.
  500. *        char ch       Character to check.
  501. *
  502. * Description    This function checks whether a character is a valid DOS
  503. *        filename character and returns 1 if so, 0 if not.
  504. *
  505. * Returns    isokay          1 if okay, 0 if illegal.
  506. *
  507. **/
  508.  
  509. static int isfc(ch)
  510. char ch;
  511. {
  512.     return (   isalpha(ch)
  513.         || isdigit(ch)
  514.         || (-1 != stschind(ch,"$&#@!%`'()-_^~")));
  515. }
  516.