]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/scriptbinder.cpp
glibc's vswprintf doesn't nul terminate on truncation.
[wxWidgets.git] / utils / HelpGen / src / scriptbinder.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: No names yet.
3 // Purpose: Contrib. demo
4 // Author: Aleksandras Gluchovas
5 // Modified by:
6 // Created: 22/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Aleskandars Gluchovas
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22
23 #include <string.h>
24 #include <memory.h>
25
26 #include <stdio.h> // import sprintf() (for doubles)
27 #include <stdlib.h> // import atoi() (for integers)
28
29 #include "scriptbinder.h"
30
31 // helper functions
32
33 static size_t wx_log2(size_t nr)
34 {
35 size_t tmp = 0;
36 while (nr >= 2 )
37 {
38 nr /= 2;
39 ++tmp;
40 }
41
42 return tmp;
43 }
44
45 /***** Implementation for class ScriptStream *****/
46
47 ScriptStream::ScriptStream()
48 : m_pBuf(0),
49 m_Size(0),
50 m_Capacity(0)
51 {}
52
53 ScriptStream::~ScriptStream()
54 {
55 if ( m_pBuf ) delete m_pBuf;
56 }
57
58 void ScriptStream::WriteBytes( const void* srcBuf, size_t count )
59 {
60 if ( !count ) return;
61
62 // increase the capacity if necessary
63 if ( m_Size + count > m_Capacity )
64 {
65 m_Capacity =
66 ( 0x2 << (wx_log2( m_Size + count ) + 1 ) );
67
68 if ( m_Capacity < 128 ) m_Capacity = 128;
69
70 char* oldBuf = m_pBuf;
71
72 m_pBuf = new char[m_Capacity];
73
74 if ( oldBuf )
75 {
76 memcpy( m_pBuf, oldBuf, m_Size );
77 delete oldBuf;
78 }
79 }
80
81 // append new data
82 memcpy( &m_pBuf[m_Size], srcBuf, count );
83
84 m_Size += count;
85 }
86
87 ScriptStream& ScriptStream::operator<<( const char* str )
88 {
89 WriteBytes( str, strlen( str ) );
90
91 return *this;
92 }
93
94 ScriptStream& ScriptStream::operator<<( const wxString& str )
95 {
96 if ( str.length() < 512 )
97 {
98 char buf[512];
99 size_t len = str.length();
100
101 for( size_t i = 0; i != len; ++i )
102 buf[i] = str[i];
103
104 WriteBytes( buf, len );
105 }
106 else
107 WriteBytes( str.c_str(), str.length() );
108
109 return *this;
110 }
111
112 ScriptStream& ScriptStream::operator<<( char ch )
113 {
114 WriteBytes( &ch, 1 );
115
116 return *this;
117 }
118
119 void ScriptStream::endl()
120 {
121 char ch = '\n';
122 WriteBytes( &ch, 1 );
123 }
124
125 /***** Implementation for class ScriptTemplate *****/
126
127 ScriptTemplate::ScriptTemplate( const wxString& templateText )
128 {
129 wxString tmp = templateText;
130
131 m_TText = (char*)malloc( tmp.length() + 1 );
132
133 strcpy( m_TText, tmp.c_str() );
134 }
135
136 ScriptTemplate::~ScriptTemplate()
137 {
138 for( size_t i = 0; i != m_Vars.size(); ++i )
139
140 delete m_Vars[i];
141
142 free( m_TText );
143 }
144
145 bool ScriptTemplate::HasVar( const char* name )
146 {
147 for( size_t i = 0; i != m_Vars.size(); ++i )
148
149 if ( strcmp( m_Vars[i]->m_Name, name ) == 0 )
150
151 return 1;
152
153 return 0;
154 }
155
156 void ScriptTemplate::AddStringVar ( const char* name, int ofs )
157 {
158 m_Vars.push_back( new TVarInfo( name, ofs, TVAR_STRING ) );
159 }
160
161 void ScriptTemplate::AddIntegerVar( const char* name, int ofs )
162 {
163 m_Vars.push_back( new TVarInfo( name, ofs, TVAR_INTEGER ) );
164 }
165
166 void ScriptTemplate::AddDoubleVar ( const char* name, int ofs )
167 {
168 m_Vars.push_back( new TVarInfo( name, ofs, TVAR_DOUBLE ) );
169 }
170
171 void ScriptTemplate::AddObjectRefArray( const char* name,
172 int ofsRefToFirstObj,
173 int ofsObjSizeInt,
174 int ofsObjRefTempl
175 )
176 {
177 TArrayInfo* pInfo = new TArrayInfo( name );
178
179 m_Vars.push_back( pInfo );
180
181 pInfo->m_RefOfs = ofsRefToFirstObj;
182 pInfo->m_SizeIntOfs = ofsObjSizeInt;
183 pInfo->m_ObjRefTemplOfs = ofsObjRefTempl;
184 }
185
186 inline void ScriptTemplate::PrintVar( TVarInfo* pInfo,
187 void* dataObj,
188 ScriptStream& stm )
189 {
190 char buf[128];
191
192 switch ( pInfo->m_Type )
193 {
194 case TVAR_INTEGER :
195 {
196 sprintf(buf, "%d",*( (int*) ((char*)dataObj + pInfo->m_Ofs) ) );
197
198 stm.WriteBytes( buf, strlen(buf ) );
199 break;
200 }
201
202 case TVAR_STRING :
203 {
204 wxString& str = *( (wxString*) ((char*)dataObj+pInfo->m_Ofs) );
205
206 const char* cs = str.c_str();
207 #ifdef DEBUG_WEIRED_OFFSETS
208 cout << "DBG:: cs address is " << (int)cs << endl;
209 cout << "DBG:: str address is " << (int)(&str) << endl;
210 cout << "DBG:: dataObj points to " << (int)dataObj << endl;
211 cout << "DBG:: pInfo->m_Ofs value is " << (int)pInfo->m_Ofs << endl;
212 cout << "DBG:: d+pInfo->m_Ofs is " << (int)((char*)dataObj + pInfo->m_Ofs) << endl;
213 cout << "DBG:: pInfo->m_Name is " << pInfo->m_Name << endl;
214 cout << "DBG:: pInfo->m_Type is " << pInfo->m_Type << endl;
215 cout << "DBG:: end of dump. " << endl;
216
217 cout << "DBG:: cs value is " << endl << cs << endl;
218 #endif
219 stm.WriteBytes( cs, strlen(cs) );
220 break;
221 }
222
223 case TVAR_DOUBLE :
224 {
225 sprintf( buf, "%f",
226 *( (double*)( (char*)dataObj+pInfo->m_Ofs) ) );
227
228 stm.WriteBytes( buf, strlen(buf ) );
229 break;
230 }
231
232 case TVAR_REF_ARRAY :
233 {
234 TArrayInfo& info = *((TArrayInfo*)pInfo);
235
236 int sz = *((int*) ( (char*)dataObj+info.m_SizeIntOfs ));
237 if ( !sz )
238 {
239 // DBG::
240 int u = 0;
241 ++u;
242 break;
243 }
244
245 int* array = *((int**)( (char*)dataObj+info.m_RefOfs ));
246
247 ScriptTemplate* pRefTempl;
248
249 for( int i = 0; i != sz; ++i )
250 {
251 pRefTempl =
252 *((ScriptTemplate**)((char*)(array[i])+info.m_ObjRefTemplOfs));
253
254 pRefTempl->PrintScript( (void*)array[i], stm );
255 }
256
257 break;
258 }
259
260 default : break;
261 }
262 }
263
264 void ScriptTemplate::PrintScript( void* dataObj, ScriptStream& stm )
265 {
266 char* cur = m_TText;
267
268 // template parsing loop
269 do
270 {
271 char* start = cur;
272
273 while( *cur != '\0' && *cur != '$' ) ++cur;
274
275 // flush text collected between variables
276 stm.WriteBytes( start, cur - start );
277
278 if ( *cur == '\0' ) break;
279
280 cur += 2; // skip to the name of the var
281
282 start = cur;
283
284 while( *cur != ')' ) ++cur;
285
286 // put terminating zero temorarely
287
288 *cur = '\0';
289
290 // look up variable
291
292 size_t sz = m_Vars.size();
293 // bool found = false;
294
295 for( size_t i = 0; i != sz; ++i )
296 {
297 if ( strcmp( m_Vars[i]->m_Name, start ) == 0 )
298 {
299 PrintVar( m_Vars[i], dataObj, stm );
300
301 *cur = ')'; // remove terminating zero
302 ++cur;
303 // found = 1;
304 break;
305 }
306 }
307
308 // variable referred by template script is not
309 // registered to this tempalte object
310 // ASSERT( found );
311
312 } while(1);
313 }
314
315 /***** implementation for class ScriptSection *****/
316
317 int ScriptSection::m_IdCounter = 0;
318
319 ScriptSection::ScriptSection( const wxString& name,
320 const wxString& body,
321 ScriptTemplate* pSectionTemplate,
322 ScriptTemplate* pReferenceTemplate,
323 bool autoHide,
324 bool sorted
325 )
326 : m_pParent ( NULL ),
327
328 m_Name ( name ),
329 m_Body ( body ),
330
331 m_AutoHide ( autoHide ),
332 m_SortOn ( sorted ),
333
334 m_pSectTempl( pSectionTemplate ),
335 m_pRefTempl ( pReferenceTemplate ),
336
337 m_RefCount( 0 ),
338 m_ArrSize( 0 )
339 {
340 // generate GUID
341
342 wxChar buf[32];
343 wxSprintf( buf, _T("%d"), ++m_IdCounter );
344 m_Id = buf;
345 }
346
347 ScriptSection::~ScriptSection()
348 {
349 SectListT lst = m_Subsections;
350
351 while( m_Subsections.size() )
352
353 m_Subsections[0]->RemoveRef();
354
355 for( size_t i = 0; i != m_References.size(); ++i )
356
357 m_References[i]->RemoveRef();
358 }
359
360 void ScriptSection::AddRef()
361 {
362 ++m_RefCount;
363 }
364
365 void ScriptSection::RemoveRef()
366 {
367 if ( !m_RefCount || !(--m_RefCount) )
368 {
369 if (m_pParent)
370 {
371 // remove ourselves from parent's list
372
373 SectListT& lst = m_pParent->m_Subsections;
374 for( size_t i = 0; i != lst.size(); ++i )
375
376 if ( lst[i] == this )
377 {
378 lst.erase( &lst[i] );
379 break;
380 }
381 }
382
383 delete this;
384 }
385 }
386
387 ScriptSection* ScriptSection::GetSubsection( const char* name )
388 {
389 // FOR NOW:: fixed section name length
390 char buf[128];
391
392 size_t cur = 0;
393
394 while( name[cur] && name[cur] != '/' )
395 {
396 buf[cur] = name[cur];
397 ++cur;
398 }
399
400 // ASSERT( cur < sizeof(buf) );
401
402 buf[cur] = '\0';
403
404 size_t sz = m_Subsections.size();
405
406 for( size_t i = 0; i != sz; ++i )
407 {
408 // DBG::
409 //ScriptSection& sect = *m_Subsections[i];
410
411 if ( m_Subsections[i]->m_Name == buf )
412 {
413 if ( name[cur] == '/' )
414
415 // search recursivelly
416 return m_Subsections[i]->GetSubsection( &name[cur+1] );
417 else
418 return m_Subsections[i];
419 }
420 }
421
422 return 0;
423 }
424
425 void ScriptSection::AddSection( ScriptSection* pSection,
426 bool addToReferencesToo
427 )
428 {
429 m_Subsections.push_back( pSection );
430
431 pSection->AddRef();
432
433 // can add section to multiple containers
434 // ASSERT( pSection->m_pParent == 0 );
435
436 pSection->m_pParent = this;
437
438 if ( addToReferencesToo )
439
440 AddReference( pSection );
441 }
442
443 void ScriptSection::AddReference( ScriptSection* pReferredSection )
444 {
445 m_References.push_back( pReferredSection );
446
447 pReferredSection->AddRef();
448
449 // set up mandatory fields used by ScriptTemplate
450 m_ArrSize = m_References.size();
451 if ( m_ArrSize )
452 m_RefFirst = (void*)&m_References[0];
453 }
454
455 SectListT& ScriptSection::GetSubsections()
456 {
457 return m_Subsections;
458 }
459
460 // static method:
461 void ScriptSection::RegisterTemplate( ScriptTemplate& sectionTempalte )
462 {
463 int nameOfs, bodyOfs, idOfs,
464 arrRefOfs, arrSizeOfs, refTemplOfs;
465
466 // obtaining offsets of member vars
467
468 GET_VAR_OFS( ScriptSection, m_Name, &nameOfs )
469 GET_VAR_OFS( ScriptSection, m_Body, &bodyOfs )
470 GET_VAR_OFS( ScriptSection, m_Id, &idOfs )
471 GET_VAR_OFS( ScriptSection, m_RefFirst,&arrRefOfs )
472 GET_VAR_OFS( ScriptSection, m_ArrSize, &arrSizeOfs )
473
474 GET_VAR_OFS( ScriptSection, m_pRefTempl, &refTemplOfs )
475
476 // registering member variables with given script template
477
478 sectionTempalte.AddStringVar( "NAME", nameOfs );
479 sectionTempalte.AddStringVar( "BODY", bodyOfs );
480 sectionTempalte.AddStringVar( "ID", idOfs );
481
482 sectionTempalte.AddObjectRefArray( "REFLIST",
483 arrRefOfs, arrSizeOfs, refTemplOfs );
484 }
485
486 void ScriptSection::Print( ScriptStream& stm )
487 {
488 // TBD:: sorting
489
490 // print out this content first
491 if ( m_pSectTempl )
492
493 m_pSectTempl->PrintScript( this, stm );
494
495 // attach contents subsections at the end of this content
496
497 for( size_t i = 0; i != m_Subsections.size(); ++i )
498
499 m_Subsections[i]->Print( stm );
500 }
501
502 void ScriptSection::DoRemoveEmptySections(int& nRemoved, SectListT& removedLst)
503 {
504 for( size_t i = 0; i != m_Subsections.size(); ++i )
505 {
506 ScriptSection& sect = *m_Subsections[i];
507
508 sect.DoRemoveEmptySections( nRemoved, removedLst );
509
510 if (sect.m_AutoHide )
511
512 if ( sect.m_References.size() == 0 )
513 {
514 bool found = false;
515 for( size_t k = 0; k != removedLst.size(); ++k )
516
517 if ( removedLst[k] == &sect )
518 {
519 found = 1;
520 break;
521 }
522
523 if ( !found )
524 {
525 removedLst.push_back( &sect );
526 ++nRemoved;
527
528 delete &sect;
529 --i;
530 }
531 }
532 }
533 }
534
535 void ScriptSection::DoRemoveDeadLinks( SectListT& removedLst)
536 {
537 size_t dsz = removedLst.size();
538
539 for( size_t i = 0; i != m_Subsections.size(); ++i )
540 {
541 m_Subsections[i]->DoRemoveDeadLinks( removedLst );
542 }
543
544 for( size_t n = 0; n != m_References.size(); ++n )
545 {
546 for( size_t k = 0; k != dsz; ++k )
547
548 if ( removedLst[k] == m_References[n] )
549 {
550 m_References.erase( &m_References[n] );
551 --n;
552
553 // set up mandatory fields used by ScriptTemplate
554 m_ArrSize = m_References.size();
555 if ( m_ArrSize )
556 m_RefFirst = (void*)&m_References[0];
557
558 break;
559 }
560 }
561 }
562
563
564 void ScriptSection::RemoveEmptySections()
565 {
566 // FIXME:: this is very_very_very slow alg.! +"doesn't work"
567
568 int nRemoved = 0;
569
570 do
571 {
572 SectListT removedLst;
573 nRemoved = 0;
574
575 DoRemoveEmptySections( nRemoved, removedLst );
576
577 DoRemoveDeadLinks( removedLst );
578 }
579 while( nRemoved );
580 }
581
582 /***** Iimplementation for class DocGeneratorBase *****/
583
584 bool DocGeneratorBase::SaveDocument( const char* fname,
585 const char* fopenOptions,
586 ScriptSection* pFromSection
587 )
588 {
589 FILE* fp = fopen( fname, fopenOptions );
590
591 if ( !fp ) return 0;
592
593 ScriptStream stm;
594
595 // check if derived class agrees about saving it
596 if ( !OnSaveDocument( stm ) ) return 0;
597
598 if ( pFromSection )
599
600 pFromSection->Print( stm );
601 else
602 {
603 ScriptSection* pTopSect = GetTopSection();
604 // ASSERT( pTopSect );
605 pTopSect->Print( stm );
606 }
607
608 size_t nWrite = fwrite( stm.GetBuf(), 1, stm.GetBufSize(), fp );
609
610 if ( nWrite != stm.GetBufSize() ) return 0;
611
612 fclose( fp );
613
614 return 1;
615
616 // that^s it
617 }