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