← Back to team overview

kicad-developers team mailing list archive

Re: PATCH: 3D resolver

 

The attached revised patch (against r6710) adds support for $(ENV_VAR)
and fixes the bug reported by easyw: "invalid filename" on MSWin when
manually editing a file name containing ${ENV_VAR}.

- Cirilo


On Sat, Apr 23, 2016 at 5:29 PM, jp charras <jp.charras@xxxxxxxxxx> wrote:

> Le 23/04/2016 09:16, Cirilo Bernardo a écrit :
> > Thanks Jean-Pierre,  I'll go through the 3D model code and make sure it
> can
> > use "${" and "$(".  I'm not sure about "%{", "%(" though since that
> wouldn't
> > be portable.  In principle there can also be multiple substitutions and a
> > substitution in the middle of a string such as
> > "/path/${VAR1}/${VAR2}/model.wrl"
> > but I think the current system should work fine; if people do something
> else
> > they can be responsible for the resulting behavior.
> >
> > - Cirilo
>
> Supporting both "${" and "$(" is enough for me.
> If "${" is the starting point, "}" is the end of var name, and for "$("
> the end of var name is ")"
>
>
> >
> > On Sat, Apr 23, 2016 at 4:28 PM, jp charras <jp.charras@xxxxxxxxxx>
> wrote:
> >
> >> Le 23/04/2016 01:29, Cirilo Bernardo a écrit :
> >>> Hi Wayne,
> >>>
> >>>  The attached patch was made against r6709.
> >>>
> >>> - Cirilo
> >>
> >> Cirilo, I just had a look at the patch, and noticed a (minor) issue:
> >> you are searching an ENV VAR by searching "${"
> >>
> >> This is perfectly true, but incomplete.
> >> wxWidgets accepts both "${" and "$(". (and on windows accept also "%{"
> and
> >> "%(" and more)
> >> The delimiters can be { and } or ( and )
> >> They are equivalent in wxWidgets, and allows using ) or } in paths
> >>
> >> (have a look at wxString wxExpandEnvVars(const wxString& str) in
> >> src/common/config.cpp, perhaps it
> >> could be used )
> >>
> >>
> >> This in important because we are using both in Kicad.
> >> for instance the fp lib wizard uses $(xxx) and in docs we use ${xxx}
> >> notation
> >>
> >> Thanks.
> >>
> >>>
> >>>
> >>> On Sat, Apr 23, 2016 at 12:52 AM, Wayne Stambaugh <
> stambaughw@xxxxxxxxx>
> >>> wrote:
> >>>
> >>>> Cirilo,
> >>>>
> >>>> This patch no longer applies cleanly.  Would you please fix it an
> repost
> >>>> it.
> >>>>
> >>>> Thanks,
> >>>>
> >>>> Wayne
> >>>>
> >>
> >>
> >> --
> >> Jean-Pierre CHARRAS
> >>
> >> _______________________________________________
> >> Mailing list: https://launchpad.net/~kicad-developers
> >> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> >> Unsubscribe : https://launchpad.net/~kicad-developers
> >> More help   : https://help.launchpad.net/ListHelp
> >>
> >
>
>
> --
> Jean-Pierre CHARRAS
>
=== modified file '3d-viewer/3d_cache/3d_cache.cpp'
--- 3d-viewer/3d_cache/3d_cache.cpp	2016-04-21 07:19:08 +0000
+++ 3d-viewer/3d_cache/3d_cache.cpp	2016-04-22 22:44:34 +0000
@@ -225,19 +225,23 @@
     if( mi != m_CacheMap.end() )
     {
         wxFileName fname( full3Dpath );
-        wxDateTime fmdate = fname.GetModificationTime();
         bool reload = false;
 
-        if( fmdate != mi->second->modTime )
+        if( fname.FileExists() )
         {
-            unsigned char hashSum[20];
-            getSHA1( full3Dpath, hashSum );
-            mi->second->modTime = fmdate;
+            wxDateTime fmdate = fname.GetModificationTime();
 
-            if( !isSHA1Same( hashSum, mi->second->sha1sum ) )
+            if( fmdate != mi->second->modTime )
             {
-                mi->second->SetSHA1( hashSum );
-                reload = true;
+                unsigned char hashSum[20];
+                getSHA1( full3Dpath, hashSum );
+                mi->second->modTime = fmdate;
+
+                if( !isSHA1Same( hashSum, mi->second->sha1sum ) )
+                {
+                    mi->second->SetSHA1( hashSum );
+                    reload = true;
+                }
             }
         }
 

=== modified file '3d-viewer/3d_cache/3d_filename_resolver.cpp'
--- 3d-viewer/3d_cache/3d_filename_resolver.cpp	2016-04-21 07:19:08 +0000
+++ 3d-viewer/3d_cache/3d_filename_resolver.cpp	2016-04-23 22:41:18 +0000
@@ -96,10 +96,9 @@
     if( m_Paths.empty() )
     {
         S3D_ALIAS al;
-        al.m_alias = _( "(DEFAULT)" );
-        al.m_pathvar = _( "${PROJDIR}" );
+        al.m_alias = "${KIPRJMOD}";
+        al.m_pathvar = "${KIPRJMOD}";
         al.m_pathexp = m_curProjDir;
-        al.m_description = _( "Current project directory" );
         m_Paths.push_back( al );
         m_NameMap.clear();
 
@@ -177,6 +176,7 @@
     m_Paths.push_back( lpath );
     wxFileName fndummy;
     wxUniChar psep = fndummy.GetPathSeparator();
+    bool hasKISYS3DMOD = false;
 
     // iterate over the list of internally defined ENV VARs
     // and add existing paths to the resolver
@@ -202,7 +202,15 @@
                 continue;
             }
 
-            fndummy.Assign( mS->second.GetValue(), "" );
+            wxString pathVal;
+
+            // ensure system ENV VARs supercede internally defined vars
+            if( wxGetEnv( mS->first, &pathVal ) && wxDirExists( pathVal ) )
+                fndummy.Assign( pathVal, "" );
+            else
+                fndummy.Assign( mS->second.GetValue(), "" );
+
+            fndummy.Normalize();
 
             if( !fndummy.DirExists() )
             {
@@ -214,9 +222,12 @@
             tmp.Append( mS->first );
             tmp.Append( "}" );
 
+            if( tmp == "${KISYS3DMOD}" )
+                hasKISYS3DMOD = true;
+
             lpath.m_alias =  tmp;
             lpath.m_pathvar = tmp;
-            lpath.m_pathexp = mS->second.GetValue();
+            lpath.m_pathexp = fndummy.GetFullPath();
 
             if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
                 lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
@@ -227,6 +238,30 @@
         }
     }
 
+    // special case: if KISYSMOD is not internally defined but is defined by
+    // the system, then create an entry here
+    wxString envar;
+
+    if( !hasKISYS3DMOD && wxGetEnv( "KISYS3DMOD", &envar ) )
+    {
+        lpath.m_alias = "${KISYS3DMOD}";
+        lpath.m_pathvar = "${KISYS3DMOD}";
+        fndummy.Assign( envar, "" );
+        fndummy.Normalize();
+
+        if( fndummy.DirExists() )
+        {
+            lpath.m_pathexp = fndummy.GetFullPath();
+
+            if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
+                lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
+
+            if( !lpath.m_pathexp.empty() )
+                m_Paths.push_back( lpath );
+        }
+
+    }
+
     if( !m_ConfigDir.empty() )
         readPathList();
 
@@ -268,6 +303,7 @@
 wxString S3D_FILENAME_RESOLVER::ResolvePath( const wxString& aFileName )
 {
     wxCriticalSectionLocker lock( lock3D_resolver );
+
     if( aFileName.empty() )
         return wxEmptyString;
 
@@ -289,43 +325,40 @@
     tname.Replace( wxT( "/" ), wxT( "\\" ) );
     #endif
 
-    // this case covers full paths and paths relative to
-    // the current working directory (which is not necessarily
+    wxFileName tmpFN( tname );
+
+    // in the case of absolute filenames we don't store a map item
+    if( !aFileName.StartsWith( wxT( "${" ) ) && tmpFN.IsAbsolute() )
+    {
+        if( tmpFN.FileExists() )
+            return tname;
+
+        return wxEmptyString;
+    }
+
+    tmpFN.Normalize();
+
+    // this case covers full paths, leading expanded vars, and paths
+    // relative to the current working directory (which is not necessarily
     // the current project directory)
-    if( wxFileName::FileExists( tname ) )
+    if( tmpFN.FileExists() )
     {
-        wxFileName tmp( tname );
-
-        if( tmp.Normalize() )
-            tname = tmp.GetFullPath();
-
+        tmpFN.Normalize();
+        tname = tmpFN.GetFullPath();
         m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
 
+        // special case: if a path begins with ${ENV_VAR} but is not in the
+        // resolver's path list then add it
+        if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
+            checkEnvVarPath( aFileName );
+
         return tname;
     }
 
-    // at this point aFileName:
-    // a. is a legacy ${} shortened name
-    // b. an aliased shortened name
-    // c. cannot be determined
-
-    if( aFileName.StartsWith( wxT( "${" ) ) )
+    // if a path begins with ${ENV_VAR}/$(ENV_VAR) and is not resolved then the
+    // file either does not exist or the ENV_VAR is not defined
+    if( aFileName.StartsWith( "${" ) || aFileName.StartsWith( "$(" ) )
     {
-        wxFileName tmp( aFileName );
-
-        if( tmp.Normalize() )
-        {
-            tname = tmp.GetFullPath();
-            m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
-
-            return tname;
-        }
-        else if( resolveVirtualEnv( aFileName, tname ) )
-        {
-            m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
-            return tname;
-        }
-
         if( !( m_errflags & ERRFLG_ENVPATH ) )
         {
             m_errflags |= ERRFLG_ENVPATH;
@@ -338,6 +371,10 @@
         return wxEmptyString;
     }
 
+    // at this point aFileName is:
+    // a. an aliased shortened name or
+    // b. cannot be determined
+
     std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
     std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
 
@@ -345,49 +382,48 @@
     // note: this is not necessarily the same as the current working
     // directory, which has already been checked. This case accounts
     // for partial paths which do not contain ${KIPRJMOD}.
-    if( !sPL->m_pathexp.empty() )
+    // This check is performed before checking the path relative to
+    // ${KISYS3DMOD} so that users can potentially override a model
+    // within ${KISYS3DMOD}
+    if( !sPL->m_pathexp.empty() && !tname.StartsWith( ":" ) )
     {
-        wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
-        wxString fullPath = fpath.GetPathWithSep() + tname;
+        tmpFN.Assign( sPL->m_pathexp, "" );
+        wxString fullPath = tmpFN.GetPathWithSep() + tname;
 
         if( wxFileName::FileExists( fullPath ) )
         {
-            wxFileName tmp( fullPath );
-
-            if( tmp.Normalize() )
-                tname = tmp.GetFullPath();
-
+            tmpFN.Assign( fullPath );
+            tmpFN.Normalize();
+            tname = tmpFN.GetFullPath();
             m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
 
             return tname;
         }
+
     }
 
-    // ${ENV_VAR} paths have already been checked; skip all but
-    // ${KISYS3DMOD}, since legacy behavior was to check if paths
-    // were relative to ${KISYS3DMOD}
-    while( sPL != ePL && sPL->m_alias.StartsWith( "${" ) )
+    // check the partial path relative to ${KISYS3DMOD} (legacy behavior)
+    if( !tname.StartsWith( ":" ) )
     {
-        if( sPL->m_alias == "${KISYS3DMOD}" )
+        wxFileName fpath;
+        wxString fullPath( "${KISYS3DMOD}" );
+        fullPath.Append( fpath.GetPathSeparator() );
+        fullPath.Append( tname );
+        fpath.Assign( fullPath );
+
+        if( fpath.Normalize() && fpath.FileExists() )
         {
-            wxFileName fpath( wxFileName::DirName( sPL->m_pathexp ) );
-            wxString fullPath = fpath.GetPathWithSep() + tname;
-
-            if( wxFileName::FileExists( fullPath ) )
-            {
-                wxFileName tmp( fullPath );
-
-                if( tmp.Normalize() )
-                    tname = tmp.GetFullPath();
-
-                m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
-
-                return tname;
-            }
+            tname = fpath.GetFullPath();
+            m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
+            return tname;
         }
 
+    } while( 0 );
+
+    // ${ENV_VAR} paths have already been checked; skip them
+    while( sPL != ePL && ( sPL->m_alias.StartsWith( "${" )
+        || sPL->m_alias.StartsWith( "$(" ) ) )
         ++sPL;
-    }
 
     // at this point the filename must contain an alias or else it is invalid
     wxString alias;         // the alias portion of the short filename
@@ -424,7 +460,6 @@
                     tname = tmp.GetFullPath();
 
                 m_NameMap.insert( std::pair< wxString, wxString > ( aFileName, tname ) );
-
                 return tname;
             }
         }
@@ -645,7 +680,8 @@
     std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
     std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
 
-    while( sPL != ePL && sPL->m_alias.StartsWith( "${" ) )
+    while( sPL != ePL && ( sPL->m_alias.StartsWith( "${" )
+        || sPL->m_alias.StartsWith( "$(" ) ) )
         ++sPL;
 
     wxFileName cfgpath( m_ConfigDir, S3D_RESOLVER_CONFIG );
@@ -721,47 +757,63 @@
 }
 
 
-bool S3D_FILENAME_RESOLVER::resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath )
+void S3D_FILENAME_RESOLVER::checkEnvVarPath( const wxString& aPath )
 {
-    aFullPath.clear();
-
-    if( !aFileName.StartsWith( "${" ) )
-        return false;
-
-    size_t eDelim = aFileName.find( '}' );
-
-    if( eDelim == wxString::npos || eDelim + 2 >= aFileName.length() )
-        return false;
-
-    wxString tPath = aFileName.substr( 0, eDelim + 1 );
-
+    bool useParen = false;
+
+    if( aPath.StartsWith( "$(" ) )
+        useParen = true;
+    else if( !aPath.StartsWith( "${" ) )
+        return;
+
+    size_t pEnd;
+
+    if( useParen )
+        pEnd = aPath.find( ")" );
+    else
+        pEnd = aPath.find( "}" );
+
+    if( pEnd == wxString::npos )
+        return;
+
+    wxString envar = aPath.substr( 0, pEnd + 1 );
+
+    // check if the alias exists; if not then add it to the end of the
+    // env var section of the path list
     std::list< S3D_ALIAS >::const_iterator sPL = m_Paths.begin();
     std::list< S3D_ALIAS >::const_iterator ePL = m_Paths.end();
 
     while( sPL != ePL )
     {
+        if( sPL->m_alias == envar )
+            return;
+
         if( !sPL->m_alias.StartsWith( "${" ) )
-            return false;
-
-        if( sPL->m_alias == tPath )
-        {
-            tPath.Append( aFileName.substr( eDelim + 2 ) );
-            wxFileName tFile( tPath );
-            tFile.Normalize();
-
-            if( tFile.FileExists() )
-            {
-                aFullPath = tFile.GetFullPath();
-                return true;
-            }
-
-            return false;
-        }
+            break;
 
         ++sPL;
     }
 
-    return false;
+    S3D_ALIAS lpath;
+    lpath.m_alias = envar;
+    lpath.m_pathvar = lpath.m_alias;
+    wxFileName tmpFN( lpath.m_alias, "" );
+    wxUniChar psep = tmpFN.GetPathSeparator();
+    tmpFN.Normalize();
+
+    if( !tmpFN.DirExists() )
+        return;
+
+    lpath.m_pathexp = tmpFN.GetFullPath();
+
+    if( !lpath.m_pathexp.empty() && psep == *lpath.m_pathexp.rbegin() )
+        lpath.m_pathexp.erase( --lpath.m_pathexp.end() );
+
+    if( lpath.m_pathexp.empty() )
+        return;
+
+    m_Paths.insert( sPL, lpath );
+    return;
 }
 
 
@@ -802,7 +854,7 @@
             fname.Replace( wxT( "\\" ), wxT( "/" ) );
             #endif
 
-            if( sL->m_alias.StartsWith( "${" ) )
+            if( sL->m_alias.StartsWith( "${" ) || sL->m_alias.StartsWith( "$(" ) )
             {
                 // old style ENV_VAR
                 tname = sL->m_alias;
@@ -1010,6 +1062,7 @@
 
         lpath = filename.substr( 0, pos0 );
 
+        // check the alias for restricted characters
         if( wxString::npos != lpath.find_first_of( wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" ) ) )
             return false;
 
@@ -1020,6 +1073,19 @@
     else
     {
         lpath = aFileName;
+
+        // in the case of ${ENV_VAR}|$(ENV_VAR)/path, strip the
+        // environment string before testing
+        pos0 = wxString::npos;
+
+        if( aFileName.StartsWith( "${" ) )
+            pos0 = aFileName.find( '}' );
+        else if( aFileName.StartsWith( "$(" ) )
+            pos0 = aFileName.find( ')' );
+
+        if( pos0 != wxString::npos )
+            lpath = aFileName.substr( pos0 + 1 );
+
     }
 
     if( wxString::npos != lpath.find_first_of( wxFileName::GetForbiddenChars() ) )

=== modified file '3d-viewer/3d_cache/3d_filename_resolver.h'
--- 3d-viewer/3d_cache/3d_filename_resolver.h	2016-04-17 22:35:32 +0000
+++ 3d-viewer/3d_cache/3d_filename_resolver.h	2016-04-22 22:44:34 +0000
@@ -99,12 +99,12 @@
     bool writePathList( void );
 
     /**
-     * Function resolveVirtualEnv
-     * extracts the ${ENV_VAR} component of aFileName and puts a
-     * resolved path in aFullPath if the ${ENV_VAR} exists in the
-     * alias list and the referenced file exists.
+     * Function checkEnvVarPath
+     * checks the ${ENV_VAR} component of a path and adds
+     * it to the resolver's path list if it is not yet in
+     * the list
      */
-    bool resolveVirtualEnv( const wxString& aFileName, wxString& aFullPath );
+    void checkEnvVarPath( const wxString& aPath );
 
 public:
     S3D_FILENAME_RESOLVER();

=== modified file '3d-viewer/3d_cache/dialogs/dlg_3d_pathconfig.cpp'
--- 3d-viewer/3d_cache/dialogs/dlg_3d_pathconfig.cpp	2016-04-17 22:35:32 +0000
+++ 3d-viewer/3d_cache/dialogs/dlg_3d_pathconfig.cpp	2016-04-23 21:56:57 +0000
@@ -47,7 +47,8 @@
         size_t listsize = rpaths->size();
         size_t listidx = 0;
 
-        while( rI != rE && (*rI).m_alias.StartsWith( "${" ) )
+        while( rI != rE && ( (*rI).m_alias.StartsWith( "${" )
+            || (*rI).m_alias.StartsWith( "$(" ) ) )
         {
             ++listidx;
             ++rI;

=== modified file '3d-viewer/3d_class.cpp'
--- 3d-viewer/3d_class.cpp	2016-04-06 00:32:50 +0000
+++ 3d-viewer/3d_class.cpp	2016-04-23 21:25:51 +0000
@@ -136,7 +136,8 @@
     // if the m_Shape3DName is not an absolute path the default path
     // given by the environment variable KISYS3DMOD will be used
 
-    if( m_Shape3DName.StartsWith( wxT("${") ) )
+    if( m_Shape3DName.StartsWith( wxT("${") ) ||
+        m_Shape3DName.StartsWith( wxT("$(") ) )
         m_Shape3DFullFilename = wxExpandEnvVars( m_Shape3DName );
     else
         m_Shape3DFullFilename = m_Shape3DName;


Follow ups

References