HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FS_HomeHelper.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  */
27 
28 #include <UT/UT_Assert.h>
29 #include <UT/UT_DSOVersion.h>
30 #include <UT/UT_EnvControl.h>
31 #include <UT/UT_NTStreamUtil.h>
32 #include <UT/UT_DirUtil.h>
33 #include <UT/UT_OpUtils.h>
34 #include <UT/UT_SysClone.h>
35 #include <UT/UT_WorkArgs.h>
36 #include <UT/UT_WorkBuffer.h>
37 #include "FS_HomeHelper.h"
38 
39 #define HOME_SIGNATURE "home:"
40 #define HOME_SIGNATURE_LEN 5
41 
42 // NB: The compiled dso needs to be installed somewhere in $HOUDINI_DSO_PATH
43 // under the 'fs' subdirectory. For example: $HOME/houdiniX.Y/dso/fs.
44 
45 void
47 {
51 }
52 
53 using namespace HDK_Sample;
54 
55 // ============================================================================
56 // The FS_HOMEREADER_HANDLE_OPTIONS define illustrates how to implement a file
57 // system protocol that uses '?' character in the file paths as a special marker
58 // for its own use, which conflicts with the default interpretation of '?' by
59 // FS library of a section name separator for index files.
60 // For more info, please see FS_Reader.h
61 //
62 // When FS_HOMEREADER_HANDLE_OPTIONS is not defined, the 'home:' protocol
63 // implements a much simpler mechanism that limits itself to replacing
64 // "home:" prefix with the home path. And since that simpified version does not
65 // assign any special meaning to '?' (nor '&'), it does not need to worry about
66 // explicitly handling index files (which is then handled automatically by the
67 // base classes).
68 //
69 // When FS_HOMEREADER_HANDLE_OPTIONS is defined, the 'home:' protocol
70 // implemented in this example uses '?' and '&' markers for delineating file
71 // access options. With '?' redefined as options separator, this protocol
72 // needs to establish a new convention for representing a path to an index file
73 // section (unless the protocol intentionally does not need or does not want to
74 // support direct access to index file sections). However, this example shows
75 // how this can be done, by designating the option "section=" for specifying the
76 // section name inside the index file given by the path portion before the '?'
77 // delimeter.
78 //
79 //
80 // With FS_HOMEREADER_HANDLE_OPTIONS defined, The 'home:' protocol recognizes
81 // the following options:
82 // "version" - to append the given version to the file name
83 // "ext" - to append the given extension to the file name
84 // "section" - to specify the section name in the given index file
85 // E.g.,
86 // "home:/temp?version=1_2&ext=txt" --> "~/temp-1_2.ext"
87 // "home:/temp?section=foo&ext=idx" --> "~/temp.idx?foo"
88 // "home:/temp.idx?section=foo" --> "~/temp.idx?foo"
89 // "home:/no_options.txt" --> "~/no_options.txt"
90 //
91 #define FS_HOMEREADER_HANDLE_OPTIONS
92 
93 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
94 static inline bool
95 fsFindLastOption(const char *option_name, const UT_WorkArgs &options,
96  UT_String *option_value = NULL, int *option_index = NULL)
97 {
98  UT_String option_tag;
99 
100  option_tag = option_name;
101  option_tag += "=";
102 
103  if( option_value )
104  option_value->clear();
105  if( option_index )
106  *option_index = -1;
107  for( int i = options.getArgc()-1; i >= 0; i-- )
108  {
109  UT_String token( options(i) );
110 
111  if( token.startsWith( option_tag ))
112  {
113  if( option_value )
114  token.substr( *option_value, option_tag.length() );
115  if( option_index )
116  *option_index = i;
117  return true;
118  }
119  }
120 
121  return false;
122 }
123 
124 static inline void
125 fsGetFileAndOptions( const char *source,
126  UT_String &file_str, UT_String &options_str )
127 {
128  UT_String source_str( source );
129  UT_WorkArgs list;
130  int count;
131 
132  source_str.tokenize( list, '?' );
133  count = list.getArgc();
134  UT_ASSERT( 0 <= count && count <= 2 );
135  file_str.harden( count > 0 ? list(0) : "" );
136  options_str.harden( count > 1 ? list(1) : "" );
137 }
138 
139 static inline void
140 fsAppendOptionsExceptForSkipped( UT_WorkBuffer &buff,
141  const UT_WorkArgs &options, int skip_index )
142 {
143  bool do_questionmark = true;
144  bool do_ampersand = false;
145  for( int i = 0; i < options.getArgc(); i++ )
146  {
147  if( i == skip_index )
148  continue;
149 
150  if( do_questionmark )
151  {
152  buff.append('?');
153  do_questionmark = false;
154  }
155 
156  if( do_ampersand )
157  buff.append('&');
158 
159  buff.append( options(i) );
160  do_ampersand = true;
161  }
162 }
163 
164 static inline bool
165 fsSplitPathIntoFileAndSection( const char *source,
166  UT_String &file_name, UT_String &section_name)
167 {
168  UT_String file_str, options_str;
169  UT_WorkArgs options;
170  int section_option_index;
171 
172  // Parse the source path and look for 'section' option, but search the list
173  // backwards (ie, get the last section option), which handles nested index
174  // files in similar way as the standard index file notation (ie,
175  // "home:x?section=a&section=b" is equivalent to "$HOME/x?a?b").
176  fsGetFileAndOptions( source, file_str, options_str );
177  options_str.tokenize( options, '&' );
178  fsFindLastOption( "section", options, &section_name, &section_option_index);
179 
180  // Reconstruct the source path, but without the section name, which we
181  // just extracted from the options. This will allow accessing the index file
182  // stream and then forming the substream for that section.
183  UT_WorkBuffer buff;
184  buff.append( file_str );
185  fsAppendOptionsExceptForSkipped( buff, options, section_option_index );
186  buff.copyIntoString( file_name );
187 
188  return section_name.isstring();
189 }
190 
191 static inline void
192 fsCombineFileAndSectionIntoPath( UT_String &source,
193  const char *file_name, const char *section_name)
194 {
196  UT_String file_name_str( file_name );
197  char separator;
198 
199  // Figure out the separator: '?' if no options yet, or '&' if there are.
200  UT_ASSERT( UTisstring( file_name ) && UTisstring( section_name ));
201  separator = file_name_str.findChar('?') ? '&' : '?';
202 
203  // Copy the base file name and append the section option.
204  buffer.strcpy( file_name );
205  buffer.append( separator );
206  buffer.append( "section=" );
207  buffer.append( section_name );
208 
209  buffer.copyIntoString( source );
210 }
211 
212 static inline void
213 fsStripSectionOptions( UT_String &path, UT_StringArray &sections )
214 {
215  UT_String section_name;
216 
217  // Note, we can arbitrarily choose whether to put the innermost sections
218  // at the beginning or the end of 'sections' array, as long as we handle
219  // this order correctly in fsAppendSectionNames().
220  // However, just to be consistent with the convention used by
221  // UT_OpUtils::splitOpIndexFileSectionPath() and
222  // FS_Reader::splitIndexFileSectionPath() we put innermost sections
223  // at the end (in case we ever need to call these static helper functions).
224  while( fsSplitPathIntoFileAndSection( path, path, section_name ))
225  sections.insert( section_name, /*index=*/ 0 );
226 }
227 
228 static inline void
229 fsAppendSectionNames( UT_String &path, UT_StringArray &sections )
230 {
231  // Construct section names using the standard convention for index files
232  // (ie, using '?' as name separator) so that the default writer can
233  // understand the path.
234  // Note, fsStripSectionOptions() puts innermost sections at the end
235  // of the array, so we iterate forward to indeed get the last entry
236  // in the array to be the innermost section.
237  for( int i = 0; i < sections.entries(); i++ )
239  sections(i) );
240 }
241 
242 static inline void
243 fsAppendFileSuffix( UT_String &str,
244  const UT_WorkArgs &options, const char *option_name,
245  const char *separator )
246 {
247  UT_String option_value;
248 
249  if( fsFindLastOption( option_name, options, &option_value ))
250  {
251  str += separator;
252  str += option_value;
253  }
254 }
255 
256 static inline void
257 fsProcessNonSectionOptions( UT_String &source )
258 {
259  UT_String options_str;
260  UT_WorkArgs options;
261 
262  // We follow the description of the options in the comment next to the
263  // FS_HOMEREADER_HANDLE_OPTIONS define.
264  // Split the source path into file and options, and then modify the
265  // final file path according to the options.
266  // Note, there should not be any 'section' option at this point anymore,
267  // since it should have been alrwady stripped in
268  // splitIndexFileSectionPath().
269  fsGetFileAndOptions( source, source, options_str );
270  options_str.tokenize( options, '&' );
271  UT_ASSERT( ! fsFindLastOption( "section", options ));
272  fsAppendFileSuffix( source, options, "version", "-" );
273  fsAppendFileSuffix( source, options, "ext", "." );
274 }
275 
276 #endif // FS_HOMEREADER_HANDLE_OPTIONS
277 
278 // ============================================================================
279 static inline void
280 fsPrefixPathWithHome(UT_String &path)
281 {
282  UT_WorkBuffer buff;
283 
284  // Substitute 'home:' prefix with home path.
285  UT_ASSERT( path.length() >= HOME_SIGNATURE_LEN );
287  if( path(HOME_SIGNATURE_LEN) != '/' )
288  buff.append('/');
289  buff.append( &path.buffer()[HOME_SIGNATURE_LEN] );
290 
291  buff.copyIntoString(path);
292 }
293 
294 static bool
295 fsConvertToStandardPathForWrite(UT_String &destpath, const char *srcpath)
296 {
297  // Handle only the 'home:' protocol paths.
298  if( strncmp(srcpath, HOME_SIGNATURE, HOME_SIGNATURE_LEN) != 0 )
299  return false;
300 
301  destpath = srcpath;
302 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
303  // For writing, we convert the "section=" options for inex files in 'home:'
304  // protocol into the standard notation because the FS_WriterStream class,
305  // used by FS_HomeWriteHelper, can write to index files only when
306  // specified in that standard notation.
307  // Alternatively, we could derive own class from FS_WriterStream and handle
308  // writing to index files there. However, since 'home:' protocol really
309  // refers to the disk files, we take advantage of the FS_WriterStream
310  // base class capabilities.
311  UT_StringArray sections;
312  fsStripSectionOptions( destpath, sections );
313  fsProcessNonSectionOptions( destpath );
314  fsAppendSectionNames( destpath, sections );
315 #endif
316 
317  fsPrefixPathWithHome(destpath);
318  return true;
319 }
320 
321 static bool
322 fsConvertToStandardPathForRead(UT_String &destpath, const char *srcpath)
323 {
324  // Handle only the 'home:' protocol paths.
325  if( strncmp(srcpath, HOME_SIGNATURE, HOME_SIGNATURE_LEN) != 0 )
326  return false;
327 
328  destpath = srcpath;
329 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
330  // For read, no need to handle index file sections, since there should not
331  // be any in the 'srcpath' because they were stripped in
332  // splitIndexFileSectionPath(). So for reading only, there is no need to
333  // implement some of the static functions that are needed only for writing.
334  // In particular, there is no need for fsAppendSectionNames() which builds
335  // the file path and appends section names using standard '?' notation.
336  fsProcessNonSectionOptions( destpath );
337 #endif
338 
339  fsPrefixPathWithHome(destpath);
340  return true;
341 }
342 
343 static bool
344 fsConvertToStandardPathForInfo(UT_String &destpath, const char *srcpath)
345 {
346 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
347  // For info, the FS_Info class already handles index files implicitly
348  // (via FS_ReadHelper), and calls this info helper class only for non-index
349  // files. So here, the srcpath should not be pointing to any index file
350  // section, just like in the case of fsConvertToStandardPathForRead().
351  // So call the conversion function for reading.
352 #endif
353  return fsConvertToStandardPathForRead(destpath, srcpath);
354 }
355 
356 // ============================================================================
358 {
360 }
361 
363 {
364 }
365 
367 FS_HomeReadHelper::createStream(const char *source, const UT_Options *)
368 {
369  FS_ReaderStream *is = 0;
370  UT_String homepath;
371 
372  if( fsConvertToStandardPathForRead(homepath, source) )
373  is = new FS_ReaderStream(homepath);
374 
375  return is;
376 }
377 
378 bool
379 FS_HomeReadHelper::splitIndexFileSectionPath(const char *source_section_path,
380  UT_String &index_file_path,
381  UT_String &section_name)
382 {
383 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
384  // We should be splitting only our own paths.
385  if( !source_section_path ||
386  strncmp(source_section_path, HOME_SIGNATURE, HOME_SIGNATURE_LEN) != 0 )
387  return false;
388 
389  // If the index file section sparator (ie, the question mark '?') has
390  // a special meaning in "home:" protocol, then this virtual overload is
391  // necessary to avoid the default parsing of source path when creating a
392  // stream. See the base class header comments for more details.
393  // Here, '?' separates the file path from file access options such as
394  // file version. Eg, in this example "home:myfile?version=1.2" file version
395  // is simply concatenated to the file name to form "myfile-1.2", but
396  // in principle it could use any other mechanism (like retrieveing it
397  // from a revision system or a database, etc).
398  // To support the index files the protocol has a 'section' option,
399  // "home:myfile?section=mysection", which allows accessing the section
400  // 'mysection' inside 'myfile' (equivalent to "$HOME/myfile?mysection"),
401  // or "home:myfile?version=1.2&section=mysection" (equivalent to
402  // "$HOME/myfile-1.2?mysection").
403  fsSplitPathIntoFileAndSection( source_section_path,
404  index_file_path, section_name );
405 
406  // Return true to indicate we are handling own spltting of section paths,
407  // whether or not the given path actually refers to an index file section.
408  return true;
409 #else
410  // If we are not handling '?' in any special way, then we return false.
411  // We could also remove this method althogehter from this class and
412  // rely on the base class for the default behaviour.
413  return false;
414 #endif // FS_HOMEREADER_HANDLE_OPTIONS
415 }
416 
417 bool
419  const char *index_file_path,
420  const char *section_name)
421 {
422 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
423  // We should be combining only our own paths.
424  if( strncmp(index_file_path, HOME_SIGNATURE, HOME_SIGNATURE_LEN) != 0 )
425  return false;
426 
427  // See the comments in splitIndexFileSectionPath()
428  fsCombineFileAndSectionIntoPath( source_section_path,
429  index_file_path, section_name );
430 
431  // Return true to indicate we are handling own gluing of section paths.
432  return true;
433 #else
434  // If we are not handling '?' in any special way, then we return false.
435  // We could also remove this method althogehter from this class and
436  // rely on the base class for the default behaviour.
437  return false;
438 #endif // FS_HOMEREADER_HANDLE_OPTIONS
439 }
440 
441 
442 // ============================================================================
444 {
446 }
447 
449 {
450 }
451 
454 {
455  FS_WriterStream *os = 0;
456  UT_String homepath;
457 
458  if( fsConvertToStandardPathForWrite(homepath, source) )
459  os = new FS_WriterStream(homepath);
460 
461  return os;
462 }
463 
464 // ============================================================================
466 {
468 }
469 
471 {
472 }
473 
474 bool
475 FS_HomeInfoHelper::canHandle(const char *source)
476 {
477  return (strncmp(source, HOME_SIGNATURE, HOME_SIGNATURE_LEN) == 0);
478 }
479 
480 bool
481 FS_HomeInfoHelper::hasAccess(const char *source, int mode)
482 {
483  UT_String homepath;
484 
485  if( fsConvertToStandardPathForInfo(homepath, source) )
486  {
487  FS_Info info(homepath);
488 
489  return info.hasAccess(mode);
490  }
491 
492  return false;
493 }
494 
495 bool
497 {
498  UT_String homepath;
499 
500  if( fsConvertToStandardPathForInfo(homepath, source) )
501  {
502  FS_Info info(homepath);
503 
504  return info.getIsDirectory();
505  }
506 
507  return false;
508 }
509 
510 int
511 FS_HomeInfoHelper::getModTime(const char *source)
512 {
513  UT_String homepath;
514 
515  if( fsConvertToStandardPathForInfo(homepath, source) )
516  {
517  FS_Info info(homepath);
518 
519  return info.getModTime();
520  }
521 
522  return 0;
523 }
524 
525 int64
526 FS_HomeInfoHelper::getSize(const char *source)
527 {
528  UT_String homepath;
529 
530  if( fsConvertToStandardPathForInfo(homepath, source) )
531  {
532  FS_Info info(homepath);
533 
534  return info.getFileDataSize();
535  }
536 
537  return 0;
538 }
539 
540 UT_String
542 {
543  UT_String homepath;
544 
545  if( fsConvertToStandardPathForInfo(homepath, source) )
546  {
547 #ifdef FS_HOMEREADER_HANDLE_OPTIONS
548  UT_String filename_str;
549  UT_String options_str;
550  UT_String option_value;
551  UT_WorkArgs options;
552 
553  fsGetFileAndOptions( source, filename_str, options_str );
554  options_str.tokenize( options, '&' );
555  if( fsFindLastOption( "ext", options, &option_value ))
556  {
557  UT_String extension(UT_String::ALWAYS_DEEP, ".");
558  extension += option_value;
559  return extension;
560  }
561 #endif
562  return FS_InfoHelper::getExtension(homepath);
563  }
564 
565  return FS_InfoHelper::getExtension(source);
566 }
567 
568 bool
570  UT_StringArray &contents,
571  UT_StringArray *dirs)
572 {
573  UT_String homepath;
574 
575  if( fsConvertToStandardPathForInfo(homepath, source) )
576  {
577  FS_Info info(homepath);
578 
579  return info.getContents(contents, dirs);
580  }
581 
582  return false;
583 }
584 
exint insert(const UT_StringHolder &str, exint idx)
virtual bool splitIndexFileSectionPath(const char *source_section_path, UT_String &index_file_path, UT_String &section_name)
static const char * getString(UT_StrControl i)
virtual bool getIsDirectory(const char *source)
Return whether the filename is a directory.
bool hasAccess(int mode=0) const
virtual FS_ReaderStream * createStream(const char *source, const UT_Options *options)
If the filename starts with "home:", open an FS_ReaderStream.
virtual UT_String getExtension(const char *source)
virtual bool canHandle(const char *source)
Determine whether this helper can process the filename.
bool getIsDirectory() const
Returns if the path is a directory.
unsigned length(void) const
Return length of string.
Definition: UT_String.h:539
void strcpy(const char *src)
virtual bool getContents(const char *source, UT_StringArray &contents, UT_StringArray *dirs)
GLsizei const GLchar *const * path
Definition: glcorearb.h:3340
virtual bool hasAccess(const char *source, int mode)
int tokenize(char *argv[], int maxArgs, char separator)
Definition: UT_String.h:805
virtual int64 getSize(const char *source)
Get the file size in bytes.
void copyIntoString(UT_String &str) const
int64 getFileDataSize() const
Returns the file size.
void installFSHelpers()
Definition: FS_HomeHelper.C:46
GLuint buffer
Definition: glcorearb.h:659
png_uint_32 i
Definition: png.h:2877
void clear()
Reset the string to the default constructor.
Definition: UT_String.h:309
long long int64
Definition: SYS_Types.h:100
const char * buffer() const
Definition: UT_String.h:503
Class to open a file as a write stream. The class tests for a "home:" prefix and replaces it with $HO...
Definition: FS_HomeHelper.h:81
bool getContents(UT_StringArray &contents, UT_StringArray *dirs=0, UT_Array< FS_Stat > *stats=0, UT_Array< FS_Stat > *dir_stats=0)
void append(char character)
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:102
exint entries() const
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:802
virtual int getModTime(const char *source)
Get the modification timestamp (returns time_t)
void harden()
Take shallow copy and make it deep.
Definition: UT_String.h:213
Class to stat a file. The class tests for a "home:" prefix and replaces it with $HOME.
Definition: FS_HomeHelper.h:93
int getArgc() const
Definition: UT_WorkArgs.h:34
GLint GLsizei count
Definition: glcorearb.h:404
virtual FS_WriterStream * createStream(const char *source)
If the filename begins with "home:" return a new write stream.
GLenum mode
Definition: glcorearb.h:98
UT_API void UTaddAbsolutePathPrefix(const char *prefix)
A map of string to various well defined value types.
Definition: UT_Options.h:42
int substr(UT_String &buf, int index, int len=0) const
SYS_FORCE_INLINE bool UTisstring(const char *s)
Definition: UT_String.h:58
virtual UT_String getExtension(const char *source)
Definition: FS_Info.h:251
int getModTime() const
#define HOME_SIGNATURE_LEN
Definition: FS_HomeHelper.C:40
Class to open a file as a read stream. The class tests for a "home:" prefix and replaces it with $HOM...
Definition: FS_HomeHelper.h:54
bool isstring() const
Definition: UT_String.h:689
virtual bool combineIndexFileSectionPath(UT_String &source_section_path, const char *index_file_path, const char *section_name)
Class for retrieving file information.
Definition: FS_Info.h:111
#define HOME_SIGNATURE
Definition: FS_HomeHelper.C:39
static void combineStandardIndexFileSectionPath(UT_String &source_section_path, const char *index_file_path, const char *section_name)
bool startsWith(const char *prefix, bool case_sensitive=true, exint len=-1) const