source: dev/trunk/abdev/BasicCompiler_Common/Compile.cpp@ 250

Last change on this file since 250 was 250, checked in by dai_9181, 17 years ago

Selectステートメントのスケジュール機構をリファクタリング

File size: 17.1 KB
Line 
1#include "stdafx.h"
2
3#include <jenga/include/smoothie/Smoothie.h>
4#include <jenga/include/smoothie/LexicalAnalysis.h>
5#include <jenga/include/smoothie/SmoothieException.h>
6
7#include <LexicalScope.h>
8#include <CodeGenerator.h>
9#include <Compiler.h>
10#include <NamespaceSupporter.h>
11
12#include "../BasicCompiler_Common/common.h"
13
14#ifdef _AMD64_
15#include "../BasicCompiler64/opcode.h"
16#else
17#include "../BasicCompiler32/opcode.h"
18#endif
19
20#include <Exception.h>
21
22//ラベルアドレス
23LABEL *pLabelNames;
24int MaxLabelNum;
25
26//グローバル変数初期バッファ
27BYTE *initGlobalBuf;
28
29//With情報
30WITHINFO WithInfo;
31
32
33
34///////////////////////////////////////////////////
35// トークンを取得
36///////////////////////////////////////////////////
37void GetIdentifierToken( char *token, const char *source, int &pos ){
38 for( int i=0; ; i++, pos++ ){
39 if( ! IsVariableChar( source[pos] ) ){
40 token[i] = 0;
41 break;
42 }
43 token[i] = source[pos];
44 }
45}
46
47
48///////////////////////////////////////////////////
49// 対になっているステートメントを飛び越す
50// ※グローバル領域用
51///////////////////////////////////////////////////
52int JumpStatement(const char *source, int &pos){
53 if( source[pos] != 1 ) return 0;
54
55 if( ! IsCommandDelimitation( source[pos - 1] ) ){
56 //直前がコマンド区切りではない場合
57 return 0;
58 }
59
60 char cStatement = source[pos + 1];
61
62 char cEnd = GetEndXXXCommand( cStatement );
63 if( cEnd == 0 ) return 0;
64
65 pos += 2;
66 while( ! ( source[pos] == 1 && source[pos + 1] == cEnd ) ){
67
68 if( source[pos] == '\0' ){
69 char temporary[64];
70 GetDefaultNameFromES( cStatement, temporary );
71 SetError( 22, temporary, pos );
72 return -1;
73 }
74
75 pos++;
76 }
77 if( ! ( source[pos] == '\0' || source[pos + 2] == '\0' ) ){
78 pos += 2;
79 }
80
81 return 1;
82}
83
84
85void NextLine(void){
86 extern HANDLE hHeap;
87 extern int MaxLineInfoNum;
88 extern LINEINFO *pLineInfo;
89 if(MaxLineInfoNum){
90 if(pLineInfo[MaxLineInfoNum-1].TopObp==obp){
91 pLineInfo[MaxLineInfoNum-1].TopCp=cp;
92 return;
93 }
94 }
95 pLineInfo=(LINEINFO *)HeapReAlloc(hHeap,0,pLineInfo,(MaxLineInfoNum+1)*sizeof(LINEINFO));
96 pLineInfo[MaxLineInfoNum].TopCp=cp;
97 pLineInfo[MaxLineInfoNum].TopObp=obp;
98
99 extern BOOL bDebugSupportProc;
100 extern BOOL bSystemProc;
101 pLineInfo[MaxLineInfoNum].dwCodeType=0;
102 if(bDebugSupportProc)
103 pLineInfo[MaxLineInfoNum].dwCodeType|=CODETYPE_DEBUGPROC;
104 if(bSystemProc)
105 pLineInfo[MaxLineInfoNum].dwCodeType|=CODETYPE_SYSTEMPROC;
106
107 MaxLineInfoNum++;
108}
109
110void ChangeOpcode(char *Command){
111 extern HANDLE hHeap;
112
113 if(Command[0]=='\0')
114 {
115 return;
116 }
117
118 trace_for_sourcecodestep( FormatEscapeSequenceStringToDefaultString(Command) );
119
120 if(Command[0]=='*'&&IsVariableTopChar(Command[1])){
121 //Goto先ラベル
122 pLabelNames=(LABEL *)HeapReAlloc(hHeap,0,pLabelNames,(MaxLabelNum+1)*sizeof(LABEL));
123 pLabelNames[MaxLabelNum].pName=(char *)HeapAlloc(hHeap,0,lstrlen(Command+1)+1);
124 lstrcpy(pLabelNames[MaxLabelNum].pName,Command+1);
125 pLabelNames[MaxLabelNum].address=obp;
126 MaxLabelNum++;
127
128 //書き込みスケジュール
129 std::vector<GotoLabelSchedule>::iterator it = compiler.codeGenerator.gotoLabelSchedules.begin();
130 while( it != compiler.codeGenerator.gotoLabelSchedules.end() )
131 {
132 if( it->GetName() == Command+1 )
133 {
134 *((long *)( OpBuffer + it->GetNativeCodePos() ))=obp-(it->GetNativeCodePos()+sizeof(long));
135
136 //詰める
137 it = compiler.codeGenerator.gotoLabelSchedules.erase( it );
138 }
139 else
140 {
141 it++;
142 }
143 }
144 return;
145 }
146 if(Command[0]==1){
147 switch(Command[1]){
148 case ESC_CONST:
149 OpcodeDim(Command+2, DIMFLAG_CONST);
150 break;
151
152 case ESC_TYPEDEF:
153 if( UserProc::IsLocalAreaCompiling() ){
154 // ローカル領域をコンパイルしているとき
155 SetError(65,"TypeDef",cp );
156 }
157
158 //既に収集済み
159 break;
160
161 case ESC_STATIC:
162 OpcodeDim(Command+2,DIMFLAG_STATIC);
163 break;
164
165 case ESC_IF:
166 OpcodeIf(Command+2);
167 break;
168 case ESC_EXITWHILE:
169 {
170 LexicalScope *pScope = compiler.codeGenerator.lexicalScopes.SearchScope( LexicalScope::SCOPE_TYPE_WHILE );
171 if( !pScope ){
172 SetError(12,"Exit While",cp);
173 return;
174 }
175 pScope->Break();
176 }
177 break;
178 case ESC_EXITFOR:
179 {
180 LexicalScope *pScope = compiler.codeGenerator.lexicalScopes.SearchScope( LexicalScope::SCOPE_TYPE_FOR );
181 if( !pScope ){
182 SetError(12,"Exit For",cp);
183 return;
184 }
185 pScope->Break();
186 }
187 break;
188 case ESC_EXITDO:
189 {
190 LexicalScope *pScope = compiler.codeGenerator.lexicalScopes.SearchScope( LexicalScope::SCOPE_TYPE_DO );
191 if( !pScope ){
192 SetError(12,"Exit Do",cp);
193 return;
194 }
195 pScope->Break();
196 }
197 break;
198 case ESC_CONTINUE:
199 OpcodeContinue();
200 break;
201
202 case ESC_EXITSUB:
203 case ESC_EXITFUNCTION:
204 case ESC_EXITMACRO:
205 OpcodeExitSub();
206 break;
207
208 case ESC_SELECTCASE:
209 OpcodeSelect(Command+2);
210 break;
211 case ESC_CASE:
212 case ESC_CASEELSE:
213 OpcodeCase(Command+2);
214 break;
215
216 case ESC_WITH:
217 extern WITHINFO WithInfo;
218
219 WithInfo.ppName=(char **)HeapReAlloc(hHeap,0,WithInfo.ppName,(WithInfo.num+1)*sizeof(char **));
220 WithInfo.ppName[WithInfo.num]=(char *)HeapAlloc(hHeap,0,lstrlen(Command+2)+1);
221 lstrcpy(WithInfo.ppName[WithInfo.num],Command+2);
222
223 WithInfo.pWithCp=(int *)HeapReAlloc(hHeap,0,WithInfo.pWithCp,(WithInfo.num+1)*sizeof(int));
224 WithInfo.pWithCp[WithInfo.num]=cp;
225
226 WithInfo.num++;
227 break;
228 case ESC_ENDWITH:
229 if(WithInfo.num<=0){
230 SetError(12,"End With",cp);
231 return;
232 }
233 WithInfo.num--;
234 HeapDefaultFree(WithInfo.ppName[WithInfo.num]);
235 break;
236 case ESC_DECLARE:
237 if( UserProc::IsLocalAreaCompiling() ){
238 // ローカル領域をコンパイルしているとき
239 SetError(65,"Declare",cp );
240 }
241 break;
242
243 case ESC_NAMESPACE:
244 compiler.GetNamespaceSupporter().GetLivingNamespaceScopes().push_back( Command + 2 );
245 break;
246 case ESC_ENDNAMESPACE:
247 if( compiler.GetNamespaceSupporter().GetLivingNamespaceScopes().size() <= 0 ){
248 SetError(12,"End Namespace",cp);
249 }
250 compiler.GetNamespaceSupporter().GetLivingNamespaceScopes().pop_back();
251 break;
252 case ESC_IMPORTS:
253 compiler.GetNamespaceSupporter().ImportsNamespace( Command + 2 );
254 break;
255 case ESC_CLEARNAMESPACEIMPORTED:
256 compiler.GetNamespaceSupporter().GetImportedNamespaces().clear();
257 break;
258
259 //Tryによる例外処理
260 case ESC_TRY:
261 Exception::TryCommand();
262 break;
263 case ESC_CATCH:
264 Exception::CatchCommand();
265 break;
266 case ESC_FINALLY:
267 Exception::FinallyCommand();
268 break;
269 case ESC_ENDTRY:
270 Exception::EndTryCommand();
271 break;
272 case ESC_THROW:
273 Exception::ThrowCommand( Command + 2 );
274 break;
275
276 default:
277 char temporary[64];
278 GetDefaultNameFromES(Command[1],temporary);
279 SetError(30,temporary,cp);
280 break;
281 }
282 return;
283 }
284 switch(MAKEWORD(Command[1],Command[0])){
285 case COM_DIM:
286 OpcodeDim(Command+2,0);
287 break;
288 case COM_DELETE:
289 OpcodeDelete(Command+2, false);
290 break;
291 case COM_SWEEPINGDELETE:
292 OpcodeDelete(Command+2, true);
293 break;
294
295 case COM_GOTO:
296 OpcodeGoto(Command+2);
297 break;
298 case COM_WHILE:
299 OpcodeWhile(Command+2);
300 break;
301 case COM_FOR:
302 OpcodeFor(Command+2);
303 break;
304 case COM_DO:
305 OpcodeDo(Command+2);
306 break;
307
308 case COM_GOSUB:
309 OpcodeGosub(Command+2);
310 break;
311 case COM_RETURN:
312 OpcodeReturn(Command+2);
313 break;
314
315 case COM_SETDOUBLE:
316 OpcodeSetPtrData(Command+2,DEF_DOUBLE);
317 break;
318 case COM_SETSINGLE:
319 OpcodeSetPtrData(Command+2,DEF_SINGLE);
320 break;
321 case COM_SETQWORD:
322 OpcodeSetPtrData(Command+2,DEF_QWORD);
323 break;
324 case COM_SETDWORD:
325 OpcodeSetPtrData(Command+2,DEF_DWORD);
326 break;
327 case COM_SETWORD:
328 OpcodeSetPtrData(Command+2,DEF_WORD);
329 break;
330 case COM_SETBYTE:
331 OpcodeSetPtrData(Command+2,DEF_BYTE);
332 break;
333
334 case COM_DEBUG:
335 extern BOOL bDebugCompile;
336 //int 3
337 if(bDebugCompile) OpBuffer[obp++]=(char)0xCC;
338#if defined(_DEBUG)
339 else OpBuffer[obp++]=(char)0xCC;
340#endif
341 break;
342
343 case COM_LET:
344 OpcodeCalc(Command+2);
345 break;
346 default:
347 OpcodeOthers(Command);
348 break;
349 }
350}
351
352void GetGlobalDataForDll(void){
353 extern char *basbuf;
354 extern HANDLE hHeap;
355 int i2,BufferSize;
356 char *Command;
357 DWORD dwRetCode;
358
359 dwRetCode=0;
360 BufferSize=128;
361 Command=(char *)HeapAlloc(hHeap,0,BufferSize);
362 for(cp++,i2=0;;cp++,i2++){
363 if(i2>=BufferSize){
364 //バッファ領域が足りなくなった場合はバッファを増量する
365 BufferSize+=128;
366 Command=(char *)HeapReAlloc(hHeap,0,Command,BufferSize);
367 }
368 if(basbuf[cp]=='\"'){
369 Command[i2]=basbuf[cp];
370 for(cp++,i2++;;cp++,i2++){
371 if(i2>=BufferSize){
372 //バッファ領域が足りなくなった場合はバッファを増量する
373 BufferSize+=128;
374 Command=(char *)HeapReAlloc(hHeap,0,Command,BufferSize);
375 }
376 Command[i2]=basbuf[cp];
377 if(basbuf[cp]=='\"') break;
378 }
379 continue;
380 }
381 if(IsCommandDelimitation(basbuf[cp])){
382 Command[i2]=0;
383
384 if(Command[0]==1&&Command[1]==ESC_SUB){
385 i2=cp;
386 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDSUB)){
387 if(basbuf[cp]=='\0'){
388 SetError(22,"Sub",i2);
389 break;
390 }
391 cp++;
392 }
393 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
394 cp+=2;
395 i2=-1;
396 continue;
397 }
398 if(Command[0]==1&&Command[1]==ESC_FUNCTION){
399 i2=cp;
400 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDFUNCTION)){
401 if(basbuf[cp]=='\0'){
402 SetError(22,"Function",i2);
403 break;
404 }
405 cp++;
406 }
407 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
408 cp+=2;
409 i2=-1;
410 continue;
411 }
412 if(Command[0]==1&&Command[1]==ESC_MACRO){
413 i2=cp;
414 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDMACRO)){
415 if(basbuf[cp]=='\0'){
416 SetError(22,"Macro",i2);
417 break;
418 }
419 cp++;
420 }
421 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
422 cp+=2;
423 i2=-1;
424 continue;
425 }
426 if(Command[0]==1&&Command[1]==ESC_TYPE){
427 i2=cp;
428 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDTYPE)){
429 if(basbuf[cp]=='\0'){
430 SetError(22,"Type",i2);
431 break;
432 }
433 cp++;
434 }
435 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
436 cp+=2;
437 i2=-1;
438 continue;
439 }
440 if(Command[0]==1&&Command[1]==ESC_CLASS){
441 i2=cp;
442 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDCLASS)){
443 if(basbuf[cp]=='\0'){
444 SetError(22,"Class",i2);
445 break;
446 }
447 cp++;
448 }
449 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
450 cp+=2;
451 i2=-1;
452 continue;
453 }
454 if(Command[0]==1&&Command[1]==ESC_INTERFACE){
455 i2=cp;
456 while(!(basbuf[cp]==1&&basbuf[cp+1]==ESC_ENDINTERFACE)){
457 if(basbuf[cp]=='\0'){
458 SetError(22,"Interface",i2);
459 break;
460 }
461 cp++;
462 }
463 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
464 cp+=2;
465 i2=-1;
466 continue;
467 }
468
469 //DLLのグローバルデータに必要なコマンドだけ
470 if(MAKEWORD(Command[1],Command[0])==COM_DIM)
471 OpcodeDim(Command+2,0);
472
473 // ネイティブコードバッファの再確保
474 ReallocNativeCodeBuffer();
475
476 if(basbuf[cp]=='\0') break;
477 i2=-1;
478 continue;
479 }
480 Command[i2]=basbuf[cp];
481 }
482 HeapDefaultFree(Command);
483}
484DWORD CompileBuffer(char Return_Sequence,WORD Return_Command){
485 extern char *basbuf;
486 extern HANDLE hHeap;
487 int i,i2,i3,i4,BufferSize,ScopeStart;
488 char *Command,temporary[VN_SIZE],*temp2,temp3[32];
489 DWORD dwRetCode;
490
491 ScopeStart=cp;
492
493 dwRetCode=0;
494 BufferSize=128;
495 Command=(char *)HeapAlloc(hHeap,0,BufferSize);
496
497 for(cp++,i2=0;;cp++,i2++){
498 if(i2>=BufferSize){
499 //バッファ領域が足りなくなった場合はバッファを増量する
500 BufferSize+=128;
501 Command=(char *)HeapReAlloc(hHeap,0,Command,BufferSize);
502 }
503 if(basbuf[cp]=='\"'){
504 Command[i2]=basbuf[cp];
505 for(cp++,i2++;;cp++,i2++){
506 if(i2>=BufferSize){
507 //バッファ領域が足りなくなった場合はバッファを増量する
508 BufferSize+=128;
509 Command=(char *)HeapReAlloc(hHeap,0,Command,BufferSize);
510 }
511 Command[i2]=basbuf[cp];
512 if(basbuf[cp]=='\"') break;
513 }
514 continue;
515 }
516 if(IsCommandDelimitation(basbuf[cp])){
517 Command[i2]=0;
518
519 if(Command[0]==1&&Command[1]==ESC_LINENUM){
520 for(i=2,i2=0;;i++,i2++){
521 if(Command[i]==','){
522 temporary[i2]=0;
523 break;
524 }
525 temporary[i2]=Command[i];
526 }
527 i3=atoi(temporary);
528 i4=i+1;
529
530 //Goto先ラベル
531 pLabelNames=(LABEL *)HeapReAlloc(hHeap,0,pLabelNames,(MaxLabelNum+1)*sizeof(LABEL));
532 pLabelNames[MaxLabelNum].pName=0;
533 pLabelNames[MaxLabelNum].line=i3;
534 pLabelNames[MaxLabelNum].address=obp;
535 MaxLabelNum++;
536
537 //書き込みスケジュール
538 std::vector<GotoLabelSchedule>::iterator it = compiler.codeGenerator.gotoLabelSchedules.begin();
539 while( it != compiler.codeGenerator.gotoLabelSchedules.end() )
540 {
541 if( it->GetName().size() == 0 && it->GetLineNum() == i3 )
542 {
543 *((long *)( OpBuffer + it->GetNativeCodePos() ))=obp-(it->GetNativeCodePos()+sizeof(long));
544
545 //詰める
546 it = compiler.codeGenerator.gotoLabelSchedules.erase( it );
547 }
548 else
549 {
550 it++;
551 }
552 }
553
554 temp2=(char *)HeapAlloc(hHeap,0,lstrlen(Command+i4)+1);
555 lstrcpy(temp2,Command+i4);
556 lstrcpy(Command,temp2);
557 HeapDefaultFree(temp2);
558 }
559
560 if(Command[0]==1&&
561 (((Command[1]==ESC_VIRTUAL||Command[1]==ESC_OVERRIDE)&&Command[2]==1&&(Command[3]==ESC_SUB||Command[3]==ESC_FUNCTION))||
562 Command[1]==ESC_SUB||
563 Command[1]==ESC_FUNCTION||
564 Command[1]==ESC_MACRO||
565 Command[1]==ESC_TYPE||
566 Command[1]==ESC_CLASS||
567 Command[1]==ESC_INTERFACE||
568 Command[1]==ESC_ENUM||
569 (Command[1]==ESC_CONST&&Command[2]==1&&Command[3]==ESC_ENUM)
570 )
571 ){
572 if(Command[1]==ESC_VIRTUAL||Command[1]==ESC_OVERRIDE||Command[1]==ESC_CONST){
573 GetDefaultNameFromES(Command[3],temporary);
574 }
575 else{
576 GetDefaultNameFromES(Command[1],temporary);
577 }
578 if(Return_Sequence){
579 SetError(12,temporary,cp);
580 break;
581 }
582
583 if(Command[1]==ESC_CONST) i3=GetEndXXXCommand(Command[3]);
584 else i3=GetEndXXXCommand(Command[1]);
585 for(i2=cp;;cp++){
586 if(basbuf[cp]==1){
587 if(basbuf[cp+1]==i3) break;
588 if(Command[1]==ESC_CLASS||Command[1]==ESC_INTERFACE){
589 //クラス、インターフェイスではSub、Functionの定義を可能にしておく
590 if(basbuf[cp+1]==ESC_MACRO||
591 basbuf[cp+1]==ESC_TYPE||
592 basbuf[cp+1]==ESC_CLASS||
593 basbuf[cp+1]==ESC_INTERFACE||
594 basbuf[cp+1]==ESC_ENUM){
595 GetDefaultNameFromES(basbuf[cp+1],temp3);
596 SetError(12,temp3,cp);
597 }
598 }
599 else{
600 if(basbuf[cp-1]!='*'&&(
601 basbuf[cp+1]==ESC_VIRTUAL||
602 basbuf[cp+1]==ESC_OVERRIDE||
603 basbuf[cp+1]==ESC_SUB||
604 basbuf[cp+1]==ESC_FUNCTION||
605 basbuf[cp+1]==ESC_MACRO||
606 basbuf[cp+1]==ESC_TYPE||
607 basbuf[cp+1]==ESC_CLASS||
608 basbuf[cp+1]==ESC_INTERFACE||
609 basbuf[cp+1]==ESC_ENUM)){
610 GetDefaultNameFromES(basbuf[cp+1],temp3);
611 SetError(12,temp3,cp);
612 }
613 }
614 }
615 if(basbuf[cp]=='\0'){
616 //error
617 //既にエラー発行済みのため、何もせずに抜ける
618 break;
619 }
620 }
621 if(basbuf[cp+2]=='\0'||basbuf[cp]=='\0') break;
622 cp+=2;
623 i2=-1;
624 continue;
625 }
626
627 if(Command[0]==0x10||Command[0]==0x11){
628 //Wend、Next、Loopなど
629 if(Return_Command==MAKEWORD(Command[1],Command[0])){
630 if(Return_Command==COM_NEXT){
631 //Nextの場合は、パラメータ(省略化)の整合性を判断する必要がある(OpcodeFor関数を参照)
632 extern char szNextVariable[VN_SIZE];
633 if(Command[2]) lstrcpy(szNextVariable,Command+2);
634 else szNextVariable[0]=0;
635 }
636 break;
637 }
638 }
639
640 NextLine();
641
642 if(Command[0]==1){
643 if(Return_Sequence==ESC_ENDIF&&Command[1]==ESC_ELSE){
644 dwRetCode=ESC_ELSE;
645 break;
646 }
647
648 if(Command[1]==Return_Sequence){
649 dwRetCode=Command[1];
650 break;
651 }
652 }
653
654 try
655 {
656 ChangeOpcode(Command);
657 }
658 catch( const SmoothieException &smoothieException )
659 {
660 SetError(
661 smoothieException.GetErrorCode(),
662 smoothieException.GetKeyword(),
663 smoothieException.GetNowLine()
664 );
665 }
666
667
668 epi_check();
669
670
671 //コンパイルを中断するとき
672 extern BOOL bStopCompile;
673 if(bStopCompile) return 0;
674
675 ReallocNativeCodeBuffer();
676
677 if(basbuf[cp]=='\0'){
678 switch(Return_Command){
679 case COM_WEND:
680 SetError(4,"\"While\" - \"Wend\" ",ScopeStart);
681 break;
682 case COM_NEXT:
683 SetError(4,"\"For\" - \"Next\" ",ScopeStart);
684 break;
685 case COM_LOOP:
686 SetError(4,"\"Do\" - \"Loop\" ",ScopeStart);
687 break;
688 }
689 switch(Return_Sequence){
690 case ESC_ENDSUB:
691 SetError(4,"\"Sub\" - \"End Sub\" ",ScopeStart);
692 break;
693 case ESC_ENDFUNCTION:
694 SetError(4,"\"Function\" - \"End Function\" ",ScopeStart);
695 break;
696 case ESC_ENDMACRO:
697 SetError(4,"\"Macro\" - \"End Macro\" ",ScopeStart);
698 break;
699 case ESC_ENDIF:
700 SetError(22,"If",ScopeStart);
701 break;
702 }
703 break;
704 }
705 i2=-1;
706 continue;
707 }
708 Command[i2]=basbuf[cp];
709 }
710 HeapDefaultFree(Command);
711
712 return dwRetCode;
713}
Note: See TracBrowser for help on using the repository browser.