1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 OUTPUT <output file name>
33 #include "nel/misc/types_nl.h"
34 #include "nel/misc/path.h"
35 #include "nel/misc/file.h"
36 #include "nel/misc/smart_ptr.h"
37 #include "nel/misc/command.h"
38 #include "nel/misc/common.h"
39 #include "nel/misc/path.h"
40 //#include "nel/memory/memory_manager.h"
41 #include "nel/misc/i18n.h"
42 #include "nel/misc/sstring.h"
43 #include "nel/misc/algo.h"
45 #include "nel/georges/u_form.h"
46 #include "nel/georges/u_form_elm.h"
47 #include "nel/georges/u_form_dfn.h"
48 #include "nel/georges/u_form_loader.h"
49 #include "nel/georges/load_form.h"
51 // Georges, bypassing interface
52 #include "nel/georges/form.h"
64 using namespace NLMISC
;
66 using namespace NLGEORGES
;
72 void setOutputFile(char *);
73 void addField(char *);
74 void addSource(char *);
75 void scanFiles(std::string extension
);
76 void executeScriptFile(const string
&);
88 UFormElm::TEval _evaluated
;
89 CField(const std::string
&name
, UFormElm::TEval eval
)
90 : _name(name
), _evaluated(eval
)
93 std::vector
<CField
> fields
;
94 std::vector
<std::string
> files
;
96 vector
<string
> inputScriptFiles
;
97 vector
<string
> inputCsvFiles
;
98 vector
<string
> inputSheetPaths
;
99 bool inputSheetPathLoaded
= false;
100 map
<string
, string
> inputSheetPathContent
;
102 const char *SEPARATOR
= ";";
103 const char *ARRAY_SEPARATOR
= "|";
110 explicit CDfnField (const std::string
&name
) : _isAnArray(false), _name(name
)
113 CDfnField (const std::string
&name
, const bool &isAnArray
) : _isAnArray(isAnArray
), _name(name
)
116 virtual ~CDfnField ()
119 bool operator <(const CDfnField
&other
) const
121 return _name
<other
._name
;
124 bool operator ==(const CDfnField
&other
) const
126 return _name
==other
._name
;
129 const std::string
&getName () const
134 const bool &isAnArray () const
145 /** Replace _false_ and _true_ with true and false
146 * this is used because excell force true and false in
147 * uppercase when is save the file in cvs mode.
149 void replaceTrueAndFalseTagFromCsv(vector
<string
> &args
)
151 for (uint i
=0; i
<args
.size(); ++i
)
153 CSString str
= args
[i
];
155 str
= str
.replace("_false_", "false");
156 str
= str
.replace("_true_", "true");
162 /** Replace false and true with _false_ and _true_
163 * this is used because excell force true and false in
164 * uppercase when is save the file in cvs mode.
165 * NB : this do the opposite jobs of the previous function
167 void replaceTrueAndFalseTagToCsv(string
&arg
)
171 str
.replace("false", "_false_");
172 str
.replace("true", "_true_");
179 Some routines for dealing with script input
181 void setOutputFile(const CSString
&filename
)
185 Outf
= nlfopen(filename
.c_str(), "wt");
188 fprintf(stderr
, "Can't open output file '%s' ! aborting.", filename
.c_str());
195 void addField(const CSString
&name
)
197 fields
.push_back(CField(name
, UFormElm::Eval
));
200 void addSource(const CSString
&name
)
202 fields
.push_back(CField(name
, UFormElm::NoEval
));
205 void buildFileVector(std::vector
<std::string
> &filenames
, const std::string
&filespec
)
208 // split up the filespec into chains
209 CSString filters
= filespec
;
211 std::vector
<std::string
> in
, out
;
213 while (!filters
.empty())
215 CSString filter
= filters
.strtok(" \t");
222 in
.push_back(filter
.leftCrop(1)); break;
225 out
.push_back(filter
.leftCrop(1)); break;
228 fprintf(stderr
,"Error in '%s' : filter must start with '+' or '-'\n",
235 /* for (i=0;i<filespec.size();)
237 for (j=i;j<filespec.size() && filespec[j]!=' ' && filespec[j]!='\t';j++) {}
241 in.push_back(filespec.substr(i+1,j-i-1)); break;
243 out.push_back(filespec.substr(i+1,j-i-1)); break;
245 fprintf(stderr,"Filter must start with '+' or '-'\n",&(filespec[i])); getchar(); exit(1);
248 while (i<filespec.size() && (filespec[i]==' ' || filespec[i]=='\t')) i++; // skip white space
251 // use the filespec as a filter while we build the sheet file vector
252 for (i
=0;i
<files
.size();i
++)
256 // make sure the filename includes all of the include strings
257 for (j
=0;j
<in
.size() && ok
;j
++)
259 if (!testWildCard(CFile::getFilename(files
[i
]), in
[j
]))
265 // make sure the filename includes none of the exclude strings
266 for (j
=0;j
<out
.size() && ok
;j
++)
268 if (testWildCard(CFile::getFilename(files
[i
]), out
[j
]))
274 // if the filename matched all of the above criteria then add it to the list
277 printf("Added: %s\n",CFile::getFilename(files
[i
]).c_str());
278 filenames
.push_back(files
[i
]);
281 printf("Found: %u matching files (from %u)\n",(uint
)filenames
.size(),(uint
)files
.size());
286 void addQuotesRoundString (std::string
&valueString
)
288 // add quotes round strings
289 std::string hold
=valueString
;
292 for (uint i
=0;i
<hold
.size();i
++)
297 valueString
+=hold
[i
];
302 void setErrorString (std::string
&valueString
, const UFormElm::TEval
&evaluated
, const UFormElm::TWhereIsValue
&where
)
304 if (evaluated
==UFormElm::NoEval
)
308 case UFormElm::ValueForm
: valueString
="ValueForm"; break;
309 case UFormElm::ValueParentForm
: valueString
="ValueParentForm"; break;
310 case UFormElm::ValueDefaultDfn
: valueString
="ValueDefaultDfn"; break;
311 case UFormElm::ValueDefaultType
: valueString
="ValueDefaultType"; break;
312 default: valueString
="ERR";
326 Scanning the files ... this is the business!!
328 void scanFiles(const CSString
&filespec
)
330 std::vector
<std::string
> filenames
;
332 buildFileVector(filenames
, filespec
);
334 // if there's no file, nothing to do
335 if (filenames
.empty())
338 // display the table header line
339 fprintf(Outf
,"FILE");
340 for (uint i
=0;i
<fields
.size();i
++)
341 fprintf(Outf
,"%s%s",SEPARATOR
, fields
[i
]._name
.c_str());
344 UFormLoader
*formLoader
= NULL
;
345 NLMISC::TTime last
= NLMISC::CTime::getLocalTime ();
346 NLMISC::TTime start
= NLMISC::CTime::getLocalTime ();
348 NLMISC::CSmartPtr
<UForm
> form
;
351 for (uint j
= 0; j
< filenames
.size(); j
++)
353 if (NLMISC::CTime::getLocalTime () > last
+ 5000)
355 last
= NLMISC::CTime::getLocalTime ();
358 nlinfo ("%.0f%% completed (%d/%d), %d seconds remaining", (float)j
*100.0/filenames
.size(),j
,filenames
.size(), (filenames
.size()-j
)*(last
-start
)/j
/1000);
363 //std::string p = NLMISC::CPath::lookup (filenames[j], false, false);
364 std::string p
= filenames
[j
];
365 if (p
.empty()) continue;
367 // create the georges loader if necessary
368 if (formLoader
== NULL
)
370 WarningLog
->addNegativeFilter("CFormLoader: Can't open the form file");
371 formLoader
= UFormLoader::createLoader ();
374 // Load the form with given sheet id
375 // form = formLoader->loadForm (sheetIds[j].toString().c_str ());
376 form
= formLoader
->loadForm (filenames
[j
].c_str ());
379 // the form was found so read the true values from George
381 fprintf(Outf
,"%s",CFile::getFilenameWithoutExtension(filenames
[j
]).c_str());
382 for (uint i
=0;i
<fields
.size();i
++)
384 UFormElm::TWhereIsValue where
;
385 UFormElm
*fieldForm
=NULL
;
386 std::string valueString
;
388 form
->getRootNode ().getNodeByName(&fieldForm
, fields
[i
]._name
);
392 if (fieldForm
->isArray()) // if its an array
394 uint arraySize
=0,arrayIndex
=0;
395 fieldForm
->getArraySize(arraySize
);
396 while (arrayIndex
<arraySize
)
398 if (fieldForm
->getArrayValue(valueString
,arrayIndex
,fields
[i
]._evaluated
, &where
))
399 ;//addQuotesRoundString (valueString);
401 setErrorString (valueString
, fields
[i
]._evaluated
, where
);
404 if (arrayIndex
<arraySize
) // another value in the array..
405 valueString
+=ARRAY_SEPARATOR
;
411 if (form
->getRootNode ().getValueByName(valueString
,fields
[i
]._name
, fields
[i
]._evaluated
, &where
)) //fieldForm->getValue(valueString,fields[i]._evaluated))
412 ;//addQuotesRoundString (valueString);
414 setErrorString (valueString
, fields
[i
]._evaluated
, where
);
417 // else // node not found.
419 // setErrorString (valueString, fields[i]._evaluated, where);
422 replaceTrueAndFalseTagToCsv(valueString
);
424 fprintf(Outf
,"%s%s", SEPARATOR
, valueString
.c_str());
426 // UFormElm::TWhereIsValue where;
428 // bool result=form->getRootNode ().getValueByName(s,fields[i]._name, fields[i]._evaluated,&where);
431 // if (fields[i]._evaluated)
439 // case UFormElm::ValueForm: s="ValueForm"; break;
440 // case UFormElm::ValueParentForm: s="ValueParentForm"; break;
441 // case UFormElm::ValueDefaultDfn: s="ValueDefaultDfn"; break;
442 // case UFormElm::ValueDefaultType: s="ValueDefaultType"; break;
451 // // add quotes round strings
452 // std::string hold=s;
455 // for (uint i=0;i<hold.size();i++)
457 // if (hold[i]=='\"')
464 // fprintf(Outf,"%s%s", SEPARATOR, s);
471 // free the georges loader if necessary
472 if (formLoader
!= NULL
)
474 UFormLoader::releaseLoader (formLoader
);
475 WarningLog
->removeFilter ("CFormLoader: Can't open the form file");
479 // sheetIds.clear ();
486 //void executeScriptBuf(char *txt)
487 void executeScriptBuf(const string
&text
)
490 CVectorSString lines
;
492 vector
<string
> tmpLines
;
493 NLMISC::explode(std::string(buf
.c_str()), std::string("\n"), tmpLines
, true);
494 lines
.resize(tmpLines
.size());
495 for (uint i
=0; i
<tmpLines
.size();i
++)
497 lines
[i
]= tmpLines
[i
];
500 for (uint i
=0; i
<lines
.size(); ++i
)
502 CSString line
= lines
[i
];
504 if (line
.empty() || line
.find("//") == 0)
506 // comment or empty line, skip
509 CSString command
= line
.strtok(" \t");
513 if (command
== "DFNPATH")
515 //CPath::getPathContent(args,true,false,true,files);
516 CPath::addSearchPath(line
, true, false); // for the dfn files
518 else if (command
== "PATH")
521 CPath::getPathContent(line
, true,false,true,files
);
522 CPath::addSearchPath(line
, true, false); // for the dfn files
524 else if (command
== "OUTPUT")
528 else if (command
== "FIELD")
532 else if (command
== "SOURCE")
536 else if (command
== "SCANFILES")
540 else if (command
== "SCRIPT")
542 executeScriptFile(line
);
546 fprintf(stderr
,"Unknown command: '%s' '%s'\n", command
.c_str(), line
.c_str());
553 void executeScriptFile(const string
&filename
)
556 CI18N::readTextFile(filename
, temp
, false, false);
560 fprintf(stderr
, "the field '%s' is empty.\n", filename
.c_str());
563 string buf
= temp
.toString();
565 executeScriptBuf(buf
);
570 if (inputSheetPathLoaded
)
573 NLMISC::createDebug();
574 NLMISC::WarningLog
->addNegativeFilter( "CPath::insertFileInMap" );
576 vector
<string
> files
;
577 vector
<string
> pathsToAdd
;
578 for (uint i
=0; i
<inputSheetPaths
.size(); ++i
)
580 explode( inputSheetPaths
[i
], std::string("*"), pathsToAdd
);
581 for ( vector
<string
>::const_iterator ip
=pathsToAdd
.begin(); ip
!=pathsToAdd
.end(); ++ip
)
583 CPath::addSearchPath( *ip
, true, false );
584 CPath::getPathContent( *ip
, true, false, true, files
);
589 for (i
=0; i
<files
.size(); ++i
)
591 string
& filename
= files
[i
];
592 // string& filebase = CFile::getFilenameWithoutExtension(filename);
593 const string
& filebase
= CFile::getFilename(filename
);
594 inputSheetPathContent
[filebase
] = filename
;
597 inputSheetPathLoaded
= true;
604 void fillFromDFN( UFormLoader
*formLoader
, set
<CDfnField
>& dfnFields
, UFormDfn
*formDfn
, const string
& rootName
, const string
& dfnFilename
)
607 for ( i
=0; i
!=formDfn
->getNumEntry(); ++i
)
609 string entryName
, rootBase
;
610 formDfn
->getEntryName( i
, entryName
);
611 rootBase
= rootName
.empty() ? "" : (rootName
+".");
613 UFormDfn::TEntryType entryType
;
615 formDfn
->getEntryType( i
, entryType
, array
);
618 case UFormDfn::EntryVirtualDfn
:
620 CSmartPtr
<UFormDfn
> subFormDfn
= formLoader
->loadFormDfn( (entryName
+ ".dfn").c_str() );
622 nlwarning( "Can't load virtual DFN %s", entryName
.c_str() );
624 fillFromDFN( formLoader
, dfnFields
, subFormDfn
, rootBase
+ entryName
, entryName
+ ".dfn" );
627 case UFormDfn::EntryDfn
:
629 UFormDfn
*subFormDfn
;
630 if ( formDfn
->getEntryDfn( i
, &subFormDfn
) )
633 formDfn
->getEntryFilename( i
, filename
);
634 fillFromDFN( formLoader
, dfnFields
, subFormDfn
, rootBase
+ entryName
, filename
); // recurse
638 case UFormDfn::EntryType
:
640 const std::string
finalName(rootBase
+entryName
);
641 dfnFields
.insert( CDfnField(finalName
, array
) );
642 //nlinfo( "DFN entry: %s (in %s)", (rootBase + entryName).c_str(), dfnFilename.c_str() );
651 * Clear the form to reuse it (and all contents below node)
653 void clearSheet( CForm
*form
, UFormElm
* node
)
655 ((CFormElm
*)node
)->clean();
661 * - Remove CSV carriage returns.
662 * - Ensure there is no non-ascii char (such as Excel's special blank crap), set them to ' '.
664 void eraseCarriageReturnsAndMakeBlankNonAsciiChars( string
& s
)
666 const char CR
= '\n';
667 string::size_type p
= s
.find( CR
);
668 while ( (p
=s
.find( CR
)) != string::npos
)
670 for ( p
=0; p
!=s
.size(); ++p
)
672 uint8
& c
= (uint8
&)s
[p
]; // ensure the test is unsigned
675 //nldebug( "Blanking bad char %u in '%s'", c, s.c_str() );
688 void convertCsvFile( const string
&file
, bool generate
, const string
& sheetType
)
690 const uint BUFFER_SIZE
= 16*1024;
691 char lineBuffer
[BUFFER_SIZE
];
693 vector
<string
> fields
;
696 FILE *s
= nlfopen(file
, "r");
700 fprintf(stderr
, "Can't find file %s to convert\n", file
.c_str());
704 if (!fgets(lineBuffer
, BUFFER_SIZE
, s
))
706 nlwarning("fgets() failed");
712 UFormLoader
*formLoader
= UFormLoader::createLoader ();
713 NLMISC::CSmartPtr
<CForm
> form
;
714 NLMISC::CSmartPtr
<UFormDfn
> formDfn
;
716 explode(std::string(lineBuffer
), std::string(SEPARATOR
), fields
);
718 vector
<bool> activeFields( fields
.size(), true );
720 // Load DFN (generation only)
721 set
<CDfnField
> dfnFields
;
724 formDfn
= formLoader
->loadFormDfn( (sheetType
+ ".dfn").c_str() );
726 nlerror( "Can't find DFN for %s", sheetType
.c_str() );
727 fillFromDFN( formLoader
, dfnFields
, formDfn
, "", sheetType
);
729 // Display missing fields and check fields against DFN
731 for ( i
=1; i
!=fields
.size(); ++i
)
733 eraseCarriageReturnsAndMakeBlankNonAsciiChars( fields
[i
] );
734 if ( fields
[i
].empty() )
736 nlinfo( "Skipping field #%u (empty)", i
);
737 activeFields
[i
] = false;
739 else if ( nlstricmp( fields
[i
], "parent" ) == 0 )
741 fields
[i
] = toLowerAscii( fields
[i
] );
745 set
<CDfnField
>::iterator ist
= dfnFields
.find( CDfnField(fields
[i
]) );
746 if ( ist
== dfnFields
.end() )
748 nlinfo( "Skipping field #%u (%s, not found in %s DFN)", i
, fields
[i
].c_str(), sheetType
.c_str() );
749 activeFields
[i
] = false;
753 for ( i
=1; i
!=fields
.size(); ++i
)
755 if ( activeFields
[i
] )
756 nlinfo( "Selected field: %s", fields
[i
].c_str() );
760 string addExtension
= "." + sheetType
;
761 uint dirmapLetterIndex
= std::numeric_limits
<uint
>::max();
762 bool dirmapLetterBackward
= false;
763 vector
<string
> dirmapDirs
;
764 string dirmapSheetCode
;
765 bool WriteEmptyProperties
= false, WriteSheetsToDisk
= true;
766 bool ForceInsertParents
= false;
770 // Get the directory mapping
773 CConfigFile dirmapcfg
;
774 dirmapcfg
.load( sheetType
+ "_dirmap.cfg" );
776 if ( OutputPath
.empty() )
778 CConfigFile::CVar
*path
= dirmapcfg
.getVarPtr( "OutputPath" );
780 OutputPath
= path
->asString();
781 if ( ! OutputPath
.empty() )
783 if ( OutputPath
[OutputPath
.size()-1] != '/' )
785 else if ( ! CFile::isDirectory( OutputPath
) )
786 nlwarning( "Output path does not exist" );
790 CConfigFile::CVar
*letterIndex1
= dirmapcfg
.getVarPtr( "LetterIndex" );
791 if ( letterIndex1
&& letterIndex1
->asInt() > 0 )
793 dirmapLetterIndex
= letterIndex1
->asInt() - 1;
795 CConfigFile::CVar
*letterWay
= dirmapcfg
.getVarPtr( "LetterWay" );
796 dirmapLetterBackward
= (letterWay
&& (letterWay
->asInt() == 1));
798 CConfigFile::CVar dirs
= dirmapcfg
.getVar( "Directories" );
799 for ( uint idm
=0; idm
!=dirs
.size(); ++idm
)
801 dirmapDirs
.push_back( dirs
.asString( idm
) );
802 nlinfo( "Directory: %s", dirmapDirs
.back().c_str() );
803 if ( ! CFile::isExists( OutputPath
+ dirmapDirs
.back() ) )
805 CFile::createDirectory( OutputPath
+ dirmapDirs
.back() );
809 if ( ! CFile::isDirectory( OutputPath
+ dirmapDirs
.back() ) )
811 nlwarning( "Already existing but not a directory!" );
816 nlinfo( "Mapping letter #%u (%s) of sheet name to directory", dirmapLetterIndex
+ 1, dirmapLetterBackward
?"backward":"forward" );
819 CConfigFile::CVar
*sheetCode
= dirmapcfg
.getVarPtr( "AddSheetCode" );
821 dirmapSheetCode
= sheetCode
->asString();
822 nlinfo( "Sheet code: %s", dirmapSheetCode
.c_str() );
824 if ( ! dirmapLetterBackward
)
825 dirmapLetterIndex
+= (uint
)dirmapSheetCode
.size();
827 CConfigFile::CVar
*wep
= dirmapcfg
.getVarPtr( "WriteEmptyProperties" );
829 WriteEmptyProperties
= (wep
->asInt() == 1);
830 nlinfo( "Write empty properties mode: %s", WriteEmptyProperties
? "ON" : "OFF" );
832 CConfigFile::CVar
*wstd
= dirmapcfg
.getVarPtr( "WriteSheetsToDisk" );
834 WriteSheetsToDisk
= (wstd
->asInt() == 1);
835 nlinfo( "Write sheets to disk mode: %s", WriteSheetsToDisk
? "ON" : "OFF" );
837 CConfigFile::CVar
*fiparents
= dirmapcfg
.getVarPtr( "ForceInsertParents" );
839 ForceInsertParents
= (fiparents
->asInt() == 1);
840 nlinfo( "Force insert parents mode: %s", ForceInsertParents
? "ON" : "OFF" );
842 catch (const EConfigFile
&e
)
844 nlwarning( "Problem in directory mapping: %s", e
.what() );
848 nlinfo( "Using output path: %s", OutputPath
.c_str() );
849 nlinfo( "Press a key to generate *.%s", sheetType
.c_str() );
851 nlinfo( "Generating...." );
855 nlinfo("Updating modifications (only modified fields are updated)");
857 set
<string
> newSheets
;
858 uint nbNewSheets
= 0, nbModifiedSheets
= 0, nbUnchangedSheets
= 0, nbWritten
= 0;
861 lineBuffer
[0] = '\0';
862 if (!fgets(lineBuffer
, BUFFER_SIZE
, s
))
864 nlwarning("fgets() failed");
868 explode(std::string(lineBuffer
), std::string(SEPARATOR
), args
);
873 eraseCarriageReturnsAndMakeBlankNonAsciiChars( args
[0] );
874 replaceTrueAndFalseTagFromCsv(args
);
877 if ( args
[0].empty() || (args
[0] == string(".")+sheetType
) )
880 //nldebug( "%s: %u", args[0].c_str(), args.size() );
881 string filebase
= dirmapSheetCode
+args
[0]; /*+"."+sheetType;*/
882 if (filebase
.find("."+sheetType
) == string::npos
)
884 filebase
+= "." + sheetType
;
886 filebase
= toLowerAscii(filebase
);
887 string filename
, dirbase
;
888 bool isNewSheet
=true;
890 // Locate existing sheet
891 // map<string, string>::iterator it = inputSheetPathContent.find( CFile::getFilenameWithoutExtension( filebase ) );
892 map
<string
, string
>::iterator it
= inputSheetPathContent
.find( CFile::getFilename( filebase
) );
894 if (it
== inputSheetPathContent
.end())
899 if ( ! filebase
.empty() )
901 nlwarning( "Sheet %s not found", filebase
.c_str( ));
907 // Load template sheet
908 filename
= toLowerAscii(filebase
);
909 form
= (CForm
*)formLoader
->loadForm( (string("_empty.") + sheetType
).c_str() );
912 nlerror( "Can't load sheet _empty.%s", sheetType
.c_str() );
915 // Deduce directory from sheet name
916 if ( dirmapLetterIndex
!= std::numeric_limits
<uint
>::max() )
918 if ( dirmapLetterIndex
< filebase
.size() )
922 if ( dirmapLetterBackward
)
923 letterIndex
= (uint
)(filebase
.size() - 1 - (CFile::getExtension( filebase
).size()+1)) - dirmapLetterIndex
;
925 letterIndex
= dirmapLetterIndex
;
926 c
= tolower( filebase
[letterIndex
] );
927 vector
<string
>::const_iterator idm
;
928 for ( idm
=dirmapDirs
.begin(); idm
!=dirmapDirs
.end(); ++idm
)
930 if ( (! (*idm
).empty()) && (tolower((*idm
)[0]) == c
) )
932 dirbase
= (*idm
) + "/";
936 if ( idm
==dirmapDirs
.end() )
938 nlinfo( "Directory mapping not found for %s (index %u)", filebase
.c_str(), letterIndex
);
939 dirbase
.clear(); // put into root
944 nlerror( "Can't map directory with letter #%u, greater than size of %s + code", dirmapLetterIndex
, filebase
.c_str() );
948 nlinfo( "New sheet: %s", filebase
.c_str() );
950 if ( ! newSheets
.insert( filebase
).second
)
951 nlwarning( "Found duplicate sheet: %s", filebase
.c_str() );
955 else // an existing sheet was found
958 // Load sheet (skip if failed)
960 filename
= (*it
).second
; // whole path
961 form
= (CForm
*)formLoader
->loadForm( filename
.c_str() );
964 nlwarning( "Can't load sheet %s", filename
.c_str() );
971 const UFormElm
&rootForm
=form
->getRootNode();
972 bool displayed
= false;
973 bool isModified
= false;
975 for ( i
=1; i
<args
.size ()
976 && i
<fields
.size (); ++i
)
978 const string
&var
= fields
[i
];
979 string
&val
= args
[i
];
981 eraseCarriageReturnsAndMakeBlankNonAsciiChars( val
);
983 // Skip column with inactive field (empty or not in DFN)
984 if ( (! activeFields
[i
]) )
987 // Skip setting of empty cell except if required
988 if ( (! WriteEmptyProperties
) && val
.empty() )
991 // Special case for parent sheet
992 if (var
== "parent") // already case-lowered
994 vector
<string
> parentVals
;
995 explode( val
, std::string(ARRAY_SEPARATOR
), parentVals
);
996 if ( (parentVals
.size() == 1) && (parentVals
[0].empty()) )
999 if ( (isNewSheet
|| ForceInsertParents
) && (! parentVals
.empty()) )
1001 // This is slow. Opti: insertParent() should have an option to do it without loading the form
1002 // parent have same type that this object (postulat).
1003 uint nbinsertedparents
=0;
1005 for ( uint p
=0; p
!=parentVals
.size(); ++p
)
1007 string localExtension
=(parentVals
[p
].find(addExtension
)==string::npos
)?addExtension
:"";
1008 string parentName
=parentVals
[p
]+localExtension
;
1010 CSmartPtr
<CForm
> parentForm
= (CForm
*)formLoader
->loadForm(CFile::getFilename(parentName
.c_str()).c_str());
1013 nlwarning( "Can't load parent form %s", parentName
.c_str() );
1017 form
->insertParent( p
, parentName
.c_str(), parentForm
);
1020 nbinsertedparents
++;
1024 nlinfo( "Inserted %u parent(s)", nbinsertedparents
);
1026 // NOTE: Changing the parent is not currently implemented!
1030 const UFormElm
*fieldForm
=NULL
;
1032 if (rootForm
.getNodeByName(&fieldForm
, var
))
1034 UFormDfn
*dfnForm
=const_cast<UFormElm
&>(rootForm
).getStructDfn();
1037 vector
<string
> memberVals
;
1038 explode( val
, std::string(ARRAY_SEPARATOR
), memberVals
);
1039 uint32 memberIndex
=0;
1041 while (memberIndex
<memberVals
.size())
1043 const uint currentMemberIndex
=memberIndex
;
1044 std::string memberVal
=memberVals
[memberIndex
];
1047 if (!memberVal
.empty())
1049 if (memberVal
[0] == '"')
1050 memberVal
.erase(0, 1);
1051 if (memberVal
.size()>0 && memberVal
[memberVal
.size()-1] == '"')
1052 memberVal
.resize(memberVal
.size()-1);
1054 if (memberVal
== "ValueForm" ||
1055 memberVal
== "ValueParentForm" ||
1056 memberVal
== "ValueDefaultDfn" ||
1057 memberVal
== "ValueDefaultType" ||
1063 // nlassert(fieldDfn);
1064 // virtual bool getEntryFilenameExt (uint entry, std::string &name) const = 0;
1065 // virtual bool getEntryFilename (uint entry, std::string &name) const = 0;
1071 static string
filenameTyp("filename.typ");
1075 if (dfnForm
->getEntryIndexByName (fieldIndex
, var
)) // field exists.
1077 dfnForm
->getEntryFilename(fieldIndex
,fileName
);
1078 if (fileName
==filenameTyp
)
1080 dfnForm
->getEntryFilenameExt(fieldIndex
,fileNameExt
);
1081 if ( !fileNameExt
.empty()
1082 && fileNameExt
!="*.*")
1084 string::size_type index
=fileNameExt
.find(".");
1085 if (index
==string::npos
) // not found.
1087 extension
=fileNameExt
;
1091 extension
=fileNameExt
.substr(index
+1);
1094 if (memberVal
.find(extension
)==string::npos
) // extension not found.
1096 memberVal
=NLMISC::toString("%s.%s",memberVal
.c_str(),extension
.c_str());
1108 if (dfnForm
->isAnArrayEntryByName(var
))
1114 const UFormElm
*arrayNode
= NULL
;
1115 if (fieldForm
->isArray()
1116 && fieldForm
->getArraySize(arraySize
) && arraySize
== memberVals
.size())
1119 if ( fieldForm
->getArrayValue(test
, currentMemberIndex
)
1120 && test
==memberVal
)
1126 //nldebug( "%s: %s '%s'", args[0].c_str(), var.c_str(), memberVal.c_str() );
1127 // need to put the value at the correct index.
1128 const std::string fieldName
=NLMISC::toString("%s[%u]", var
.c_str(), currentMemberIndex
).c_str();
1129 const_cast<UFormElm
&>(rootForm
).setValueByName(memberVal
, fieldName
);
1138 if ( rootForm
.getValueByName(test
, var
)
1139 && test
==memberVal
)
1145 //nldebug( "%s: %s '%s'", args[0].c_str(), var.c_str(), memberVal.c_str() );
1146 const_cast<UFormElm
&>(rootForm
).setValueByName(memberVal
, var
);
1155 nlinfo("in %s:", filename
.c_str());
1157 nlinfo("%s = %s", var
.c_str(), memberVal
.c_str());
1163 else // field Node not found :\ (bad)
1174 ++nbUnchangedSheets
;
1178 if ( isNewSheet
|| displayed
)
1180 if ( WriteSheetsToDisk
)
1183 string path
= isNewSheet
? OutputPath
: "";
1184 string ext
= (filename
.find( addExtension
) == string::npos
) ? addExtension
: "";
1185 string absoluteFileName
=path
+ dirbase
+ filename
+ ext
;
1187 // nlinfo("opening: %s", absoluteFileName.c_str() );
1188 COFile
output(absoluteFileName
);
1189 if (!output
.isOpen())
1191 nlinfo("creating path: %s", (path
+ dirbase
).c_str() );
1192 NLMISC::CFile::createDirectory(path
+ dirbase
);
1195 // nlinfo("opening2: %s", absoluteFileName.c_str() );
1196 output
.open (absoluteFileName
);
1198 if (!output
.isOpen())
1200 nlinfo("ERROR! cannot create file path: %s", absoluteFileName
.c_str() );
1204 form
->write(output
);
1207 if (!CPath::exists(filename
+ ext
))
1208 CPath::addSearchFile(absoluteFileName
);
1212 clearSheet( form
, &form
->getRootNode() );
1216 nlinfo( "%u sheets processed (%u new, %u modified, %u unchanged - %u written)", nbNewSheets
+nbModifiedSheets
+nbUnchangedSheets
, nbNewSheets
, nbModifiedSheets
, nbUnchangedSheets
, nbWritten
);
1217 UFormLoader::releaseLoader (formLoader
);
1221 void usage(char *argv0
, FILE *out
)
1224 fprintf(out
, "Syntax: %s [-p <sheet path>] [-s <field_separator>] [-g <sheet type>] [-o <output path>] [<script file name> | <csv file name>]", argv0
);
1225 fprintf(out
, "(-g = generate sheet files, needs template sheet _empty.<sheet type> and <sheet type>_dirmap.cfg in the current folder");
1227 fprintf(out
, "Script commands:\n");
1228 fprintf(out
, "\tDFNPATH\t\t<search path for george dfn files>\n");
1229 fprintf(out
, "\tPATH\t\t<search path for files to scan>\n");
1230 fprintf(out
, "\tOUTPUT\t\t<output file>\n");
1231 fprintf(out
, "\tFIELD\t\t<field in george file>\n");
1232 fprintf(out
, "\tSOURCE\t\t<field in george file>\n");
1233 fprintf(out
, "\tSCANFILES\t[+<text>|-<text>[...]]\n");
1234 fprintf(out
, "\tSCRIPT\t\t<script file to execute>\n");
1238 int main(int argc
, char* argv
[])
1240 bool generate
= false;
1243 // parse command line
1245 for (i
=1; (sint
)i
<argc
; i
++)
1247 const char *arg
= argv
[i
];
1254 if ((sint
)i
== argc
)
1256 fprintf(stderr
, "Missing <sheet path> after -p option\n");
1257 usage(argv
[0], stderr
);
1260 inputSheetPaths
.push_back(argv
[i
]);
1264 if ((sint
)i
== argc
)
1266 fprintf(stderr
, "Missing <field_separator> after -s option\n");
1267 usage(argv
[0], stderr
);
1270 SEPARATOR
= argv
[i
];
1274 if ((sint
)i
== argc
)
1276 fprintf(stderr
, "Missing <sheetType> after -g option\n");
1277 usage(argv
[0], stderr
);
1281 sheetType
= string(argv
[i
]);
1285 if ((sint
)i
== argc
)
1287 fprintf(stderr
, "Missing <output path> after -o option\n");
1288 usage(argv
[0], stderr
);
1291 OutputPath
= string(argv
[i
]);
1294 fprintf(stderr
, "Unrecognized option '%c'\n", arg
[1]);
1295 usage(argv
[0], stderr
);
1302 if (CFile::getExtension(arg
) == "csv")
1304 inputCsvFiles
.push_back(arg
);
1308 inputScriptFiles
.push_back(arg
);
1313 if (inputScriptFiles
.empty() && inputCsvFiles
.empty())
1315 fprintf(stderr
, "Missing input script file or csv file\n");
1316 usage(argv
[0], stderr
);
1322 for (i
=0; i
<inputScriptFiles
.size(); ++i
)
1323 executeScriptFile(inputScriptFiles
[i
]);
1325 for (i
=0; i
<inputCsvFiles
.size(); ++i
)
1326 convertCsvFile(inputCsvFiles
[i
], generate
, sheetType
);
1328 fprintf(stderr
,"\nDone.\n");