home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / updates / update14.zoo / lib / unx2dos.c < prev   
Encoding:
C/C++ Source or Header  |  1992-01-15  |  16.4 KB  |  644 lines

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4. #include <osbind.h>
  5. #include <device.h>
  6. #include <types.h>
  7. #include <dirent.h>
  8. #include "symdir.h"
  9. #ifndef _COMPILER_H
  10. #include <compiler.h>
  11. #endif
  12.     
  13. #ifndef NAME_MAX
  14. #  include <limits.h>
  15. #endif
  16.     
  17. #ifndef _LIB_NAME_MAX
  18. #  define _LIB_NAME_MAX NAME_MAX
  19. #endif
  20.     
  21. #ifndef TRUE
  22. #define TRUE 1
  23. #define FALSE 0
  24. #endif
  25.     
  26. char _lOK;            /* symbolic links are OK            */
  27. char *_lDIR = ".dir";        /* name of symbolic link directory file    */
  28. char _lAUTO;            /* make automatic symbolic links        */
  29. char _lHIDE;            /* hide the _lDIR file from searches        */
  30.  
  31. char _tCASE;            /* translate filenames to lower case       */
  32. char _tSLASH;            /* '/' seperates directories like '\' does */
  33. char _tDOTS;            /* if != 0, translate '.' into this        */
  34. char _tUNLIMITED;        /* filenames are not restricted to 8.3       */
  35. char _tDEV;            /* allow /dev/filename for special files   */
  36. char _tROOT;            /* use this drive as default root directory */
  37.  
  38. /* translations to assume if there is no UNIXMODE environment variable
  39.  * it's assumed that the user will set this via a definition
  40.  *    char *_default_unixmode = whatever
  41.  * if no such definition is present (i.e. _default_unixmode is 0) then
  42.  * "/" is assumed
  43.  */
  44.  
  45. char *_default_unixmode;
  46. int _unixmode = 1;    /* this will go away someday */
  47.  
  48. /*
  49.  * _set_unixmode(mode): set Unix emulation modes. Normally "mode" will come
  50.  * from the environment variable UNIXMODE. These settings should only be
  51.  * changed at the explicit request of the user!
  52.  * The characters in "mode" have the following meanings:
  53.  *     b    Open all files in binary mode (no CR, only LF).
  54.  *    c    Assume case is already significant to the operating system.
  55.  *    d    Allow special file names like /dev/console.
  56.  *    r drv    Filenames starting with '/' are rooted from this drive.
  57.  *    u    Assume the OS allows unlimited length file names already.
  58.  *    /    Allow '/' as a directory seperator.
  59.  *    . char    Translate extra dots in a filename to 'char'.
  60.  *    L    Allow symbolic links.
  61.  *
  62.  * The following characters are meaningful only if 'L' is present.
  63.  *    A    Automatically create symbolic links for files whose names
  64.  *        are changed by the _unx2dos routine (e.g. filenames that
  65.  *        don't match the TOS 8 character + 3 character extension
  66.  *        rule). Such automatic symbolic links are much "tighter"
  67.  *        than normal symbolic links, and are effectively aliases
  68.  *        for the old name.
  69.  *    H    Hide the .dir file from directory searches. Explicit
  70.  *        requests for it (e.g. unlink()) still find it, though.
  71.  */
  72.  
  73. static int _adjust __PROTO((char *name, char *result));
  74. static int _canon __PROTO((char *base, char *path, char *result, int level));
  75.  
  76. void _set_unixmode(mode)
  77. char *mode;
  78. {
  79.     char c;
  80.     
  81.     if (!mode && !(mode = _default_unixmode)) {
  82. #if 0
  83.     /* this is what we will eventually do */
  84.     mode = "/";
  85. #else
  86.     /* compatibility with older versions of the library */
  87.     switch(_unixmode) {
  88.       case 0: mode = ""; break;
  89.       case 1: mode = "/"; break;
  90.       case 2: mode = "/.,"; break;
  91.       default: mode = "/.,LAHd"; break;
  92.     }
  93. #endif
  94.     }
  95.     _tSLASH = _tDOTS = _tUNLIMITED = _tDEV = FALSE;
  96.     _tCASE = TRUE;
  97.     _tROOT = 0;
  98.     _lOK = _lHIDE = _lAUTO = FALSE;
  99.     
  100.     while (c = *mode++) {
  101.     switch(c) {
  102.       case 'b': _binmode(TRUE); break;
  103.       case 'c': _tCASE = FALSE; break;
  104.       case 'd': _tDEV = TRUE; break;
  105.       case 'u': _tUNLIMITED = TRUE; break;
  106.       case '/': _tSLASH = TRUE; break;
  107.       case 'L': _lOK = TRUE; break;
  108.       case 'A': _lAUTO = TRUE; break;
  109.       case 'H': _lHIDE = TRUE; break;
  110.       case '.':
  111.         if (*mode && !isalpha(*mode))
  112.         _tDOTS = *mode++;
  113.         break;
  114.       case 'r':
  115.         if (*mode && isalpha(*mode))
  116.         _tROOT = *mode++;
  117.       default:
  118.         break;
  119.     }
  120.     }
  121. }
  122.  
  123. /*
  124.  * _uniquefy(name): change name so that it's not the name of any existing
  125.  * file. If the file already exists, then we try changing the file's
  126.  * extension in the following ways:
  127.  * put '$' as the second character
  128.  * put '0'-'9' as the last character (with '$' still in second place).
  129.  * put 'A'-'Z' as the last character (with '$' still in second place).
  130.  * if all of the above fail, put '$$' in the last two characters and
  131.  * give up.
  132.  */
  133.  
  134. void
  135.     _uniquefy(dos)
  136. char *dos;
  137. {
  138.     char *ext, c;
  139.     struct dirent *_do_stat(char *);
  140.     
  141.     if (!_do_stat(dos))    /* file not found, so "dos" is unique */
  142.     return;
  143.     
  144.     /* make 'ext' point to the (last two characters of) the extension */
  145.     
  146.     ext = strrchr(dos, '\\'); if (!ext) ext = dos;
  147.     ext = strrchr(ext, '.');
  148.     if (ext && ext[1]) {
  149.     ext+=2;
  150.     if (!*ext) {
  151.         ext[0] = 0; ext[1] = 0;
  152.     }
  153.     }
  154.     else {
  155.     for(ext=dos; *ext; ext++);
  156.     strcpy(ext, ".$0"); ext++;
  157.     }
  158.     
  159.     *ext = '$';
  160.     if (!_do_stat(dos))
  161.     return;
  162.     
  163.     ext[2] = 0;
  164.     for (c = '0'; c <= '9'; c++) {
  165.     ext[1] = c;
  166.     if (!_do_stat(dos))
  167.         return;
  168.     }
  169.     for (c = 'A'; c <= 'Z'; c++) {
  170.     ext[1] = c;
  171.     if (!_do_stat(dos))
  172.         return;
  173.     }
  174.     /* at this point we're sunk; punt and try '$$' in the extension */
  175.     ext[1] = '$';
  176. }
  177.  
  178. /*
  179.  * adjust(name): adjusts a directory entry to be acceptable to DOS.
  180.  * if _tCASE is on, it is converted to upper case.
  181.  * if _tDOTS is on, '.' is converted to _tDOTS wherever necessary to avoid
  182.  *    conflicts with the DOS 8.3 naming convention.
  183.  * unless _tUNLIMITED is set, only an 8 character base name and 3 character
  184.  *    extension are kept.
  185.  * returns: _NM_CHANGE if the name has been transformed in some irrevocable way
  186.  *    i.e. if dos2unx cannot recover the same name
  187.  *          _NM_OK otherwise
  188.  */
  189.  
  190. static int
  191.     _adjust(name, result)
  192. char *name;
  193. char *result;
  194. {
  195.     char tmp[_LIB_NAME_MAX];
  196.     char *n, *eos, *lastdot = 0;
  197.     int count, change;
  198.     
  199. #ifdef DEBUG
  200.     printf("_adjust(%s)", name); fflush(stdout);
  201. #endif
  202.     strcpy(tmp, name);
  203.     change = 0;
  204.     
  205.     /* first, do case and dot conversion */
  206.     for (n = tmp; *n; n++) {
  207.     if (_tDOTS) {
  208.         if(*n == '.')
  209.         *n = _tDOTS;
  210.         else if (*n == _tDOTS)
  211.         change++;
  212.     }
  213.     if (_tCASE) {
  214.         if (islower(*n))
  215.         *n = toupper(*n);
  216.         else if (isupper(*n))
  217.         change++;
  218.     }
  219.     }
  220.     
  221.     eos = n;    /* end of the "tmp" string */
  222.     
  223.     /* if dots were converted, change the last one possible back into a '.'
  224.      * e.g. if _tDOTS == '_', file.c.Z -> file_c_Z -> file_c.Z
  225.      */
  226.     if (_tDOTS && !_tUNLIMITED) {
  227.     for (n = tmp; *n; n++) {
  228.         if (*n == _tDOTS) {
  229.         if ( (eos - n) <= 4 && (n - tmp) > 8 ) {
  230.             *n = '.';
  231.             goto dot_was_set;
  232.         }
  233.         else lastdot = n;
  234.         }
  235.     }
  236.     /* if we found no good place, and if the name is too long to fit without
  237.      * an extension, we just put the period in the last possible place
  238.      */
  239.     if (lastdot && (n - tmp > 8 || eos - lastdot <= 4))
  240.         *lastdot = '.' ;
  241.     }
  242.     
  243.   dot_was_set:
  244.     /*
  245.      * here we enforce the 8 character name + 3 character extension rule
  246.      */
  247.     if (_tUNLIMITED)
  248.     strcpy(result, name);
  249.     else {
  250.     eos = result;
  251.     n = tmp;
  252.     for (count = 0; count < 8; count++) {
  253.         if (!*n || *n == '.') break;
  254.         *eos++ = *n++;
  255.     }
  256.     while (*n && *n != '.') {
  257.         change++; n++;
  258.     }
  259.     if (*n == '.') *eos++ = *n++;
  260.     for (count = 0; count < 3; count++) {
  261.         if (!*n) break;
  262.         *eos++ = *n++;
  263.     }
  264.     *eos++ = 0;
  265.     change += strlen(n);
  266.     }
  267. #ifdef DEBUG
  268.     printf("->(%s)\n", result);
  269. #endif
  270.     return change ? _NM_CHANGE : _NM_OK;
  271. }
  272.  
  273. /*
  274.  * _canon(base, path, result, level):
  275.  * find a canonical form for the given path, based in the directory "base"
  276.  * (e.g. the current directory); the result is copied into "result".
  277.  * "level" is the level of recursion, and is used to prevent infinite
  278.  * loops in symbolic links.
  279.  *
  280.  * "base" must already be in a canonical form; the return value from the
  281.  * GEMDOS Dgetpath() call is almost suitable, but the drive letter must
  282.  * be prefixed to it. No checking of "base" is done.
  283.  * Note that "base" and "result" may be pointers to the same array!
  284.  *
  285.  * returns:
  286.  *    _NM_LINK    if the last component is a symbolic link
  287.  *    _NM_OK    if the name of the last component is recoverable by _dos2unx
  288.  *    _NM_CHANGE otherwise
  289.  *
  290.  * Also, the global variables __link_path and __link_name are set
  291.  * to the path of the last filename component, and the name of
  292.  * the file, respectively (the canonical form of the file name
  293.  * to is returned in result, and will NOT be __link_path\__link_name if
  294.  * __link_name was a symbolic link). If it was a symbolic link, __link_flags
  295.  * is set to the flags associated with the link (e.g. whether it was
  296.  * auto-created), and __link_to is set to the link contents, which were
  297.  * followed in the course of creating the canonical name.
  298.  *
  299.  * All this is done so that functions like "creat" and "rename" that need
  300.  * access to the path and "real" name of the file (if it was a symbolic link)
  301.  * don't have to duplicate work we've already done.
  302.  */
  303.  
  304. #define DIRSEP(p) ((p) == '\\' || (_tSLASH && (p) == '/'))
  305. #define MAXRECURSE 12
  306.  
  307. char __link_path[FILENAME_MAX], __link_name[_LIB_NAME_MAX],
  308.     __link_to[_LIB_NAME_MAX];
  309. int __link_flags = 0;
  310.  
  311. static int
  312.     _canon(base, path, result, level)
  313. char *base, *path;
  314. char *result;
  315. int level;
  316. {
  317.     char tmp[_LIB_NAME_MAX], name[_LIB_NAME_MAX];
  318.     SYMDIR *dir;
  319.     SYMENTRY *ent;
  320.     char *n;
  321.     int found, change = _NM_OK, needschange = _NM_OK;
  322.     
  323.     /* FIX_ME: we really ought to flag an error of some sort here */
  324.     if (level > MAXRECURSE) {
  325.     return _NM_OK;
  326.     }
  327.     
  328. #ifdef DEBUG
  329.     printf("_canon: [%s] + [%s]\n", base, path);
  330. #endif
  331.     if (!*path) {
  332.     if (result != base)
  333.         strcpy(result, base);
  334.     return _NM_OK;
  335.     }
  336.     
  337.     /* check for paths relative to root of current drive         */
  338.     /* if _tROOT is set, then such paths should start at _tROOT     */
  339.     
  340.     if (DIRSEP(path[0])) {
  341.     if (result != base)
  342.         strcpy(result, base);
  343.     if (_tROOT) {
  344.         if (_tCASE)
  345.         result[0] = toupper(_tROOT);
  346.         else
  347.         result[0] = _tROOT;
  348.     }
  349.     result[2] = 0;    /* the drive is already in the first part */
  350.     path++;
  351.     }
  352.     /* check for absolute paths with drive letter given */
  353.     else if (path[1] == ':') {
  354.     result[0] = _tCASE ? toupper(path[0]) : path[0];
  355.     result[1] = ':';
  356.     result[2] = 0;
  357.     path += 2;
  358.     if (DIRSEP(path[0])) {
  359.         path++;
  360.     }
  361.     }
  362.     else if (result != base)
  363.     strcpy(result, base);
  364.     
  365.     /* now path is relative to what's currently in "result" */
  366.     
  367.     while(*path) {
  368.     /* get next name in path */
  369.     n = name;
  370.     while (*path && !DIRSEP(*path))
  371.         *n++ = *path++;
  372.     *n++ = 0;
  373.     if (*path) path++;
  374.     change = _NM_OK;    /* assume this is a regular name */
  375.     
  376.     /* check for "." and ".." */
  377.     if (!strcmp(name, ".")) continue;
  378.     if (!strcmp(name, "..")) {
  379.         n = strrchr(result, '\\');
  380.         if (n) *n = 0;
  381.         continue;
  382.     }
  383.     
  384.     /* see if "name" is a symbolic link */
  385.     found = 0;
  386.     dir = _read_symdir(result);
  387. #ifdef DEBUG
  388.     if (!dir) printf("unx2dos: _read_symdir(%s) failed\n", result);
  389. #endif
  390.     ent = dir ? dir->s_dir : 0;
  391.     
  392.     while (ent) {
  393.         if (!strcmp(ent->linkname, name)) {
  394.         change = _NM_LINK;
  395.         /* only set __link_to once */
  396.         if (!__link_to[0])
  397.             strcpy(__link_to, ent->linkto);
  398. #ifdef DEBUG
  399.         printf("...following link (%s)->(%s)\n", name, ent->linkto);
  400. #endif
  401.         
  402.         if (level == 0) {
  403.             strcpy(__link_path, result);
  404.             __link_flags = ent->flags;
  405.         }
  406.         /*
  407.          * note that _free_symdir caches the directories it frees, so it's OK
  408.          * to use ent->linkto afterwards (otherwise we'd have to strdup() it.
  409.          * Also note that we *do* want to free it before recursively calling
  410.          * ourselves; otherwise it won't be in the cache.
  411.          */
  412.         found = 1;
  413.         _free_symdir(dir);
  414.         /*
  415.          * We should follow normal symbolic links all the way through, since it's
  416.          * OK to have a link to a link.
  417.          * Auto symbolic links, however, must be linked to a real GEMDOS file.
  418.          */
  419.         if (ent->flags & SD_AUTO) {
  420.             strcat(result, "\\");
  421.             _adjust(ent->linkto, tmp);
  422.             strcat(result, tmp);
  423.         }
  424.         else {
  425.             _canon(result, ent->linkto, result, level+1);
  426.         }
  427.         
  428.         break;    /* out of the search */
  429.         }
  430.         /*
  431.          * if the name we're searching for is the target of an automatic link,
  432.          * then we should have been using that name instead. to prevent
  433.          * name conflict problems, flag the name as needing to be changed;
  434.          * _unx2dos can then try to sort out what to do.
  435.          */
  436.         else if (_lAUTO && !strcmp(ent->linkto, name)) {
  437.         if (ent->flags & SD_AUTO)
  438.             needschange = _NM_CHANGE;
  439.         }
  440.         ent = ent->next;
  441.     }
  442.     
  443.     /* if found == 1, then "result" has already been appropriately updated */
  444.     /* otherwise, append "name" to path, adjusting appropriately */
  445.     if (!found) {
  446.         _free_symdir(dir);
  447.         strcat(result, "\\");
  448.         change = _adjust(name, tmp);
  449.         strcat(result, tmp);
  450.         if (needschange) change = needschange;
  451.     }
  452.     }
  453.     if (level == 0)
  454.     strcpy(__link_name, name);
  455.     return change;
  456. }
  457.  
  458. /*
  459.  * translate a Unix style filename to a DOS one 
  460.  * returns:
  461.  * _NM_DEV    if the name was of a device
  462.  * _NM_LINK     if the name is a symbolic link
  463.  * _NM_OK     if the last component can be recovered via _dos2unx
  464.  * _NM_CHANGE    if the last component is irrevocably changed
  465.  */
  466.  
  467. int
  468.     _unx2dos(unx, dos)
  469. const char *unx;
  470. char *dos;
  471. {
  472.     char path[FILENAME_MAX];
  473.     static char _old_unixmode = 1;
  474.     struct _device *d;
  475.     int change;
  476.     
  477.     /* compatibility code: will go away someday soon */
  478.     if (_unixmode != _old_unixmode) {
  479.     _old_unixmode = _unixmode;
  480.     _set_unixmode(NULL);
  481.     }
  482.     
  483.     /* check for a TOS device name */
  484.     if (d = _dev_dosname(unx)) {
  485.     strcpy(dos, unx);
  486.     return _NM_DEV;
  487.     }
  488.     /* get current directory */
  489.     path[0] = Dgetdrv() + 'A';
  490.     path[1] = ':';
  491.     (void)Dgetpath(path+2, 0);
  492.     
  493.     if (_tDEV && !strncmp(unx, "/dev/", 5)) {
  494.     /* check for a unix device name */
  495.     if (d = _dev_unxname(unx+5)) {
  496.         strcpy(dos, d->dosnm);
  497.         return _NM_DEV;
  498.     }
  499.     /* check for a path like /dev/A/somefile */
  500.     else if (isalpha(unx[5]) && (((unx[6] == '/') || (unx[6] == '\\')) || (!unx[6]))) {
  501.         path[0] = unx[5];
  502.         path[2] = 0;
  503.         unx = (unx[6]) ? &unx[7] : &unx[6];
  504.     }
  505.     }
  506.     
  507.     __link_to[0]='\0';
  508.     
  509.     change = _canon(path, (char *)unx, dos, 0);
  510.     /*
  511.      * If automatic symbolic links are on, then we should try to remap weird
  512.      * filenames into new, unique ones.
  513.      */
  514.     if (change == _NM_CHANGE && _lOK && _lAUTO) {
  515.     _uniquefy(dos);
  516.     }
  517.     return change;
  518. }
  519.  
  520. /*
  521.  * translate a DOS style filename to Unix. Note that this function does
  522.  * *NOT* look up symbolic links; for that, call _full_dos2unx.
  523.  * The return value may be given a useful purpose someday;
  524.  * for now it's always 0.
  525.  */
  526.  
  527. int
  528.     _dos2unx(dos, unx)
  529. const char *dos;
  530. char *unx;
  531. {
  532.     char c;
  533. #ifdef DEBUG
  534.     char *oldunx = unx;
  535.     printf("dos2unx(%s)->", dos); fflush(stdout);
  536. #endif
  537.     while(c = *dos++) {
  538.     if (_tSLASH && c == '\\')
  539.         *unx++ = '/';
  540.     else if (_tDOTS && c == _tDOTS)
  541.         *unx++ = '.';
  542.     else if (_tCASE)
  543.         *unx++ = tolower(c);
  544.     else
  545.         *unx++ = c;
  546.     }
  547.     *unx++ = 0;
  548. #ifdef DEBUG
  549.     printf("(%s)\n", oldunx);
  550. #endif
  551.     return 0;
  552. }
  553.  
  554. int
  555.     _full_dos2unx(dos, unx)
  556. char *dos, *unx;
  557. {
  558.     SYMDIR *dir;
  559.     SYMENTRY *ent;
  560.     char *n, *curdir, *lastslash, name[_LIB_NAME_MAX], tmp[_LIB_NAME_MAX];
  561.     char slash;
  562.     
  563.     curdir = dos;
  564.     lastslash = 0;
  565.     slash = _tSLASH ? '/' : '\\';
  566.     if (dos[0] && dos[1] == ':') {
  567.     *unx++ = tolower(*dos); dos++;
  568.     *unx++ = *dos++;
  569.     }
  570.     if (*dos == '\\') {
  571.     *unx++ = slash; lastslash = dos; dos++;
  572.     }
  573.     else if (*dos) {    /* something is wrong -- punt */
  574.     return _dos2unx(curdir, unx);
  575.     }
  576.     while (*dos) {
  577.     n = tmp;
  578.     while (*dos && *dos != '\\') {
  579.         *n++ = *dos++;
  580.     }
  581.     *n++ = 0;
  582.     _dos2unx(tmp, name);
  583.     *lastslash = 0;        /* tie off current directory */
  584.     /* see if the DOS file has a Unix alias */
  585.     dir = _read_symdir(curdir);
  586.     ent = dir ? dir->s_dir : 0;
  587.     while (ent) {
  588.         if ((ent->flags & SD_AUTO) && 
  589.         !strcmp(ent->linkto, name)) {
  590.         strcpy(name, ent->linkname);
  591.         break;
  592.         }
  593.         ent = ent->next;
  594.     }
  595.     _free_symdir(dir);
  596.     for (n = name; *n; n++)
  597.         *unx++ = *n;
  598.     if (*dos)
  599.         *unx++ = slash;
  600.     *lastslash = '\\';    /* restore path */
  601.     lastslash = dos;
  602.     if (*dos) dos++;
  603.     }
  604.     *unx++ = 0;
  605.     return 0;
  606. }
  607.  
  608. /*
  609.  * the stuff below this line is for compatibility with the old unx2dos, and
  610.  * will go away someday
  611.  */
  612.  
  613. /* system default file name mapping function pointers */
  614. typedef int (*fnmapfunc_t)(const char *, char *);
  615.  
  616. static fnmapfunc_t ux2dos = _unx2dos;
  617. static fnmapfunc_t dos2ux = _dos2unx;
  618.  
  619. /* set user specified filename mapping function
  620.  *    NULL => use system default mapping function
  621.  */
  622. void fnmapfunc(u2dos, dos2u)
  623. fnmapfunc_t u2dos, dos2u;
  624. {
  625.     ux2dos = (u2dos == NULL) ? _unx2dos : u2dos;
  626.     dos2ux = (dos2u == NULL) ? _dos2unx : dos2u;
  627. }
  628.  
  629. /* mapping functions -- call the mapping functions via pointers */
  630.  
  631. int unx2dos(u, d)
  632. const char *u;
  633. char *d;
  634. {
  635.     return (*ux2dos)(u, d);
  636. }
  637.  
  638. int dos2unx(d, u)
  639. const char *d;
  640. char *u;
  641. {
  642.     return (*dos2ux)(d, u);
  643. }
  644.