00001
00002
00003 #include <string.h>
00004 #include "gstring.h"
00005
00006
00007
00008 t_uchar gStringGeneric::myChrNul=0;
00009
00010
00011
00012
00013 gStringGeneric::gStringGeneric (eStorage kind,
00014 eStrStrategy aStgy,
00015 t_uchar* s,
00016 unsigned aMaxSize)
00017 : gStorage( kind, e_StgDefault ),
00018 size( 0 ),
00019 preMaxSize( aStgy==e_StrAlloc ? 0 : GENUCHAR_USU_BUFSIZE ),
00020 maxSize( aMaxSize ),
00021 stgy( aStgy ),
00022 str( nil ),
00023 doRevalidateSize( false )
00024 {
00025 thisPreAllocate( s, preMaxSize );
00026 }
00027
00028 gStringGeneric::~gStringGeneric ()
00029 {
00030 thisDelete();
00031 }
00032
00033 char* gStringGeneric::Str ()
00034 {
00035 return (char*)UStr();
00036 }
00037
00038 t_uchar* gStringGeneric::UStr ()
00039 {
00040 return str;
00041 }
00042
00043 t_uchar gStringGeneric::GetUChar (unsigned idx)
00044 {
00045 if ( thisIndex(idx)==false ) return myChrNul;
00046 return str[idx-1];
00047 }
00048
00049 void gStringGeneric::Reset ()
00050 {
00051 gStorage::Reset();
00052 SetEmpty();
00053 }
00054
00055 void gStringGeneric::SetEmpty ()
00056 {
00057 thisDelete();
00058 thisPreAllocate( (t_uchar*)"\0", 0 );
00059 }
00060
00061 void gStringGeneric::Set (char* s)
00062 {
00063 ASSERTION(s!=nil,"gStringGeneric::Set");
00064 Set( (t_uchar*)s );
00065 }
00066
00067 void gStringGeneric::Set (t_uchar* s)
00068 {
00069 ASSERTION(s!=nil,"gStringGeneric::Set");
00070 thisDelete();
00071 thisPreAllocate( s, preMaxSize );
00072 }
00073
00074 void gStringGeneric::Set (int v)
00075 {
00076 thisDelete();
00077 Add( v );
00078 }
00079
00080 void gStringGeneric::Set (unsigned v)
00081 {
00082 thisDelete();
00083 Add( v );
00084 }
00085
00086
00087 unsigned gStringGeneric::Add (char c)
00088 {
00089 char s[2];
00090 s[0] = c;
00091 s[1] = 0;
00092 return Add( s );
00093 }
00094
00095 unsigned gStringGeneric::Add (t_uchar c)
00096 {
00097 return Add( (char)c );
00098 }
00099
00100 unsigned gStringGeneric::Add (char* s)
00101 {
00102 return Add( (t_uchar*)s );
00103 }
00104
00105 unsigned gStringGeneric::Add (t_uchar* s)
00106 {
00107 unsigned len, totalLen;
00108 int copyCode;
00109
00110 if ( s==nil ) return size;
00111 len = strlen( (char*)s );
00112 if ( size==0 ) {
00113 thisCopy( s, len );
00114 return len;
00115 }
00116
00117 size = strlen( (char*)str );
00118 totalLen = size+len;
00119
00120 gString tempStr( str );
00121 copyCode = thisCopy( tempStr.UStr(), totalLen );
00122 if ( copyCode<0 ) {
00123 copyCode = thisCopy( tempStr.UStr(), tempStr.Length() );
00124 ASSERTION(copyCode>=0,"copyCode>=0");
00125 return size;
00126 }
00127 strcat( (char*)str, (char*)s );
00128 return size;
00129 }
00130
00131 unsigned gStringGeneric::Add (int v)
00132 {
00133 gUCharBuffer s( 30 );
00134 sprintf( (char*)s.uBuf, "%d", v );
00135 Add( s.uBuf );
00136 return size;
00137 }
00138
00139 unsigned gStringGeneric::Add (unsigned v)
00140 {
00141 gUCharBuffer s( 30 );
00142 sprintf( (char*)s.uBuf, "%u", v );
00143 Add( s.uBuf );
00144 return size;
00145 }
00146
00147 void gStringGeneric::UpString ()
00148 {
00149 unsigned k;
00150 t_uchar chr;
00151
00152 thisSize();
00153 for (k=0; k<size; k++) {
00154 chr = str[k];
00155 if ( chr>='a' && chr<='z' ) {
00156 chr = (t_uchar)(chr - 32);
00157 str[k] = chr;
00158 }
00159 }
00160 }
00161
00162 void gStringGeneric::DownString ()
00163 {
00164 unsigned k;
00165 t_uchar chr;
00166
00167 thisSize();
00168 for (k=0; k<size; k++) {
00169 chr = str[k];
00170 if ( chr>='A' && chr<='Z' ) {
00171 chr = (t_uchar)(chr + 32);
00172 str[k] = chr;
00173 }
00174 }
00175 }
00176
00177 bool gString::Trim ()
00178 {
00179 bool didTrimL = TrimLeft();
00180 bool didTrimR = TrimRight();
00181 return didTrimL || didTrimR;
00182 }
00183
00184 bool gString::TrimLeft ()
00185 {
00186 char* s = Str();
00187 int i, k=0, n = (int)Length();
00188 gString tempS;
00189 for (i=0; i<n; i++) {
00190 if ( s[i]!=' ' && s[i]!='\t' ) {
00191 break;
00192 }
00193 k++;
00194 }
00195 for ( ; i<n; i++) tempS.Add( s[i] );
00196 Copy( tempS );
00197 return k>0;
00198 }
00199
00200 bool gString::TrimRight ()
00201 {
00202 char* s = Str();
00203 int i, k, n = (int)Length();
00204
00205 for (i=n-1, k=n; i>=0; i--) {
00206 if ( s[i]!=' ' && s[i]!='\t' ) {
00207 break;
00208 }
00209 s[i] = 0;
00210 k--;
00211 }
00212 gString tempS( s );
00213 Copy( tempS );
00214 return k<n;
00215 }
00216
00217 t_uchar& gStringGeneric::operator[] (int index)
00218 {
00219 if ( thisIsValidIndex(index)==false ) return myChrNul;
00220 doRevalidateSize = true;
00221 return str[index-1];
00222 }
00223
00224 t_uchar* gStringGeneric::ToString (t_uchar* uBuf)
00225 {
00226 if ( uBuf!=nil ) {
00227 strcpy( (char*)uBuf, (char*)str );
00228 }
00229 return str;
00230 }
00231
00232 bool gStringGeneric::SaveGuts (FILE* f)
00233 {
00234 if ( CanSave( f )==false ) return false;
00235 if ( str==nil ) return true;
00236 return fprintf(f,"%s",str)==1;
00237 }
00238
00239 bool gStringGeneric::RestoreGuts (FILE* f)
00240 {
00241 unsigned n=0;
00242 t_uchar uChr;
00243 gUCharBuffer sTemp;
00244
00245 if ( CanRestore( f )==false ) return false;
00246 thisDelete();
00247
00248 while ( fscanf(f,"%c",&uChr) ) {
00249 if ( uChr=='\n' || uChr==0 ) break;
00250 sTemp.uBuf[n++] = uChr;
00251 sTemp.uBuf[n] = 0;
00252 if ( n>=sTemp.size ) return false;
00253 }
00254 thisCopy( sTemp.uBuf, n );
00255 return true;
00256 }
00257
00258 void gStringGeneric::thisPreAllocate (t_uchar* s, unsigned toSize)
00259 {
00260 if ( stgy==e_StrDefault ) stgy = e_StrAlloc;
00261 ASSERTION(stgy==e_StrAlloc,"stgy==e_StrAlloc: TODO other");
00262 ASSERTION(str==nil,"str==nil");
00263 if ( s==nil ) return;
00264 thisCopy( s, strlen( (char*)s ) );
00265 }
00266
00267 void gStringGeneric::thisDelete ()
00268 {
00269 size = 0;
00270 if ( str!=nil ) delete[] str;
00271 str = nil;
00272 }
00273
00274 int gStringGeneric::thisCopy (t_uchar* s, unsigned len)
00275 {
00276 thisDelete();
00277 ASSERTION(preMaxSize==0,"preMaxSize==0: TODO other");
00278 if ( s==nil ) return -1;
00279 if ( maxSize>0 && len>maxSize ) return -2;
00280 str = new t_uchar[len+1];
00281 ASSERTION(str!=nil,"str!=nil");
00282 strcpy( (char*)str, (char*)s );
00283 size = len;
00284 return (int)size;
00285 }
00286
00287 bool gStringGeneric::thisIndex (unsigned& idx)
00288 {
00289 bool isOk = idx>=1 && idx<=size;
00290
00291 if ( idx<1 ) idx = 1;
00292 if ( idx>size ) idx = size;
00293 isOk = idx>=1 && idx<=size;
00294
00295 return isOk;
00296 }
00297
00298 bool gStringGeneric::thisIsValidIndex (int index)
00299 {
00300 bool isOk = index>0;
00301 unsigned idx = (unsigned)index;
00302
00303 if ( isOk==false ) return false;
00304
00305 return idx>=1 && idx<=size;
00306 }
00307
00308 unsigned gStringGeneric::thisSize ()
00309 {
00310 if ( doRevalidateSize==false ) return size;
00311 size = str==nil?0:(strlen((char*)str));
00312 return size;
00313 }
00314
00315 gString::gString ()
00316 : gStringGeneric(
00317 e_String,
00318 e_StrAlloc,
00319 (t_uchar*)"\0",
00320 0)
00321 {
00322 }
00323
00324 gString::gString (gString& copy)
00325 : gStringGeneric(
00326 e_String,
00327 e_StrAlloc,
00328 (t_uchar*)(copy.Str()),
00329 0)
00330 {
00331 ASSERTION(str!=nil,"str!=nil");
00332 }
00333
00334 gString::gString (char* s)
00335 : gStringGeneric(
00336 e_String,
00337 e_StrAlloc,
00338 (t_uchar*)s,
00339 0)
00340 {
00341 ASSERTION(s!=nil,"s!=nil");
00342 }
00343
00344 gString::gString (t_uchar* s)
00345 : gStringGeneric(
00346 e_String,
00347 e_StrAlloc,
00348 s,
00349 0)
00350 {
00351 ASSERTION(s!=nil,"s!=nil");
00352 }
00353
00354 gString::gString (char c)
00355 : gStringGeneric(
00356 e_String,
00357 e_StrAlloc,
00358 (t_uchar*)"\0",
00359 0)
00360 {
00361 Add( c );
00362 }
00363
00364 gString::gString (unsigned nBytes, char c)
00365 : gStringGeneric(
00366 e_String,
00367 e_StrAlloc,
00368 (t_uchar*)"\0",
00369 nBytes)
00370 {
00371 if ( nBytes ) {
00372 thisCopy( (t_uchar*)"\0", nBytes );
00373
00374 memset( str, (int)c, (size_t)nBytes );
00375 }
00376 }
00377
00378 gString::~gString ()
00379 {
00380 }
00381
00382 bool gString::Match (gString& copy, bool doIgnoreCase)
00383 {
00384 return thisMatch( Str(), copy.Str(), doIgnoreCase )==0;
00385 }
00386
00387 bool gString::Match (char* s, bool doIgnoreCase)
00388 {
00389 return thisMatch( Str(), s, doIgnoreCase )==0;
00390 }
00391
00392 unsigned gString::Find (gString& sSub, bool doIgnoreCase)
00393 {
00394 return thisFind( Str(), sSub.Str(), 1, doIgnoreCase );
00395 }
00396
00397 unsigned gString::Find (char* s, bool doIgnoreCase)
00398 {
00399 return thisFind( Str(), s, 1, doIgnoreCase );
00400 }
00401
00402 unsigned gString::Find (char c, bool doIgnoreCase)
00403 {
00404 char s[2];
00405 s[0] = c;
00406 s[1] = 0;
00407 if ( c==0 ) return 0;
00408 return thisFind( Str(), s, 1, doIgnoreCase );
00409 }
00410
00411 unsigned gString::Find (gString& sSub, unsigned& nOcc, bool doIgnoreCase)
00412 {
00413 return thisFindFwd( Str(), sSub.Str(), 1, doIgnoreCase, nOcc );
00414 }
00415
00416 unsigned gString::Find (char* s, unsigned& nOcc, bool doIgnoreCase)
00417 {
00418 return thisFindFwd( Str(), s, 1, doIgnoreCase, nOcc );
00419 }
00420
00421 unsigned gString::Find (char c, unsigned& nOcc, bool doIgnoreCase)
00422 {
00423 char s[2];
00424 if ( c==0 ) return 0;
00425 s[0] = c;
00426 s[1] = 0;
00427 return thisFindFwd( Str(), s, 1, doIgnoreCase, nOcc );
00428 }
00429
00430 unsigned gString::FindBack (gString& sSub, bool doIgnoreCase)
00431 {
00432 unsigned nOcc;
00433 return thisFindBack( Str(), sSub.Str(), 1, doIgnoreCase, nOcc );
00434 }
00435
00436 unsigned gString::FindBack (char* s, bool doIgnoreCase)
00437 {
00438 unsigned nOcc;
00439 return thisFindBack( Str(), s, 1, doIgnoreCase, nOcc );
00440 }
00441
00442 unsigned gString::FindBack (char c, bool doIgnoreCase)
00443 {
00444 unsigned nOcc;
00445 char s[2];
00446 if ( c==0 ) return 0;
00447 s[0] = c;
00448 s[1] = 0;
00449 return thisFindBack( Str(), s, 1, doIgnoreCase, nOcc );
00450 }
00451
00452 unsigned gString::FindAnyChr (gString& b, bool doIgnoreCase)
00453 {
00454 unsigned posAny;
00455 return thisFindAny( (char*)str, b.Str(), doIgnoreCase, posAny );
00456 }
00457
00458 unsigned gString::FindAnyChr (char* s, bool doIgnoreCase)
00459 {
00460 unsigned posAny;
00461 return thisFindAny( (char*)str, s, doIgnoreCase, posAny );
00462 }
00463
00464 unsigned gString::FindAnyChr (gString& b, bool doIgnoreCase, unsigned& posAny)
00465 {
00466 return thisFindAny( (char*)str, b.Str(), doIgnoreCase, posAny );
00467 }
00468
00469 unsigned gString::FindAnyChr (char* s, bool doIgnoreCase, unsigned& posAny)
00470 {
00471 return thisFindAny( (char*)str, s, doIgnoreCase, posAny );
00472 }
00473
00474 unsigned gString::FindExcept (gString& sExcept, bool doIgnoreCase)
00475 {
00476 ;
00477
00478
00479 gString sTemp( sExcept );
00480 if ( doIgnoreCase ) sTemp.UpString();
00481 return thisFindExcept( Str(), sTemp.Str(), 1, doIgnoreCase );
00482 }
00483
00484 unsigned gString::FindExcept (char* s, bool doIgnoreCase)
00485 {
00486 gString sTemp( s );
00487 if ( doIgnoreCase ) sTemp.UpString();
00488 return thisFindExcept( Str(), sTemp.Str(), 1, doIgnoreCase );
00489 }
00490
00491 unsigned gString::AddString (gString& a)
00492 {
00493 if ( a.IsEmpty() ) return 0;
00494 return Add( a.Str() );
00495 }
00496
00497 void gString::Copy (gString& copy)
00498 {
00499 Set( copy.Str() );
00500 }
00501
00502 gString& gString::CopyFromTo (gString& copy, unsigned startPos, unsigned endPos)
00503 {
00504 unsigned i, k, len = copy.Length();
00505
00506 SetEmpty();
00507 if ( startPos==0 ) startPos = 1;
00508 if ( endPos==0 ) endPos = len;
00509 if ( endPos>len ) endPos = len;
00510 if ( startPos>endPos ) return *this;
00511
00512 thisCopy( (t_uchar*)"\0", 1+endPos-startPos );
00513
00514 for (i=startPos, k=0; i<=endPos; i++) {
00515 str[k++] = copy[i];
00516 }
00517 str[k] = 0;
00518 doRevalidateSize = true;
00519
00520 return *this;
00521 }
00522
00523 unsigned gString::Delete (unsigned startPos, unsigned endPos)
00524 {
00525 unsigned
00526 oldSize=size,
00527 i, k,
00528 p0=startPos==0?1:startPos,
00529 p1=endPos==0?size:gMIN(endPos,size);
00530
00531 if ( startPos==0 && endPos==0 ) {
00532 SetEmpty();
00533 return oldSize;
00534 }
00535
00536 if ( p0>size ) return 0;
00537
00538 gString sTemp;
00539 for (i=1, k=0; i<p0; i++, k++) sTemp.Add( (char)str[k] );
00540 for (i=p1+1, k=p1; i<=size; i++, k++) sTemp.Add( (char)str[k] );
00541
00542 Copy( sTemp );
00543 ASSERTION(oldSize>=size,"oldSize>=size");
00544 oldSize -= size;
00545
00546 return oldSize;
00547 }
00548
00549 unsigned gString::Insert (gString& copy, unsigned startPos)
00550 {
00551 unsigned p0=startPos==0?1:startPos;
00552 gString sTemp;
00553 gString sCopy( Str() );
00554
00555 if ( p0>1 ) {
00556 sTemp = CopyFromTo( sCopy, 1, p0-1 );
00557 }
00558 sTemp.AddString( copy );
00559 sTemp += CopyFromTo( sCopy, p0 );
00560 *this = sTemp;
00561 return 0;
00562 }
00563
00564 gString& gString::operator= (gString& copy)
00565 {
00566 Copy( copy );
00567 return *this;
00568 }
00569
00570 gString& gString::operator= (char*s)
00571 {
00572 ASSERTION(s!=nil,"s!=nil");
00573 gString copy( s );
00574 Copy( copy );
00575 return *this;
00576 }
00577
00578 gString& gString::operator= (char c)
00579 {
00580 char s[2];
00581 s[0] = c;
00582 s[1] = 0;
00583 Set( s );
00584 return *this;
00585 }
00586
00587 gString& gString::operator= (int v)
00588 {
00589 char s[40];
00590 snprintf(s,40,"%d",v);
00591 Set( s );
00592 return *this;
00593 }
00594
00595 gString& gString::operator+ (gString& copy)
00596 {
00597 char* s = copy.Str();
00598 Add( s );
00599 return *this;
00600 }
00601
00602 gString& gString::operator+= (gString& copy)
00603 {
00604 AddString( copy );
00605 return *this;
00606 }
00607
00608 gStorage* gString::NewObject ()
00609 {
00610 gString* a = new gString( str );
00611 return a;
00612 }
00613
00614 t_uchar* gString::ToString (t_uchar* uBuf)
00615 {
00616 return gStringGeneric::ToString( uBuf );
00617 }
00618
00619 bool gString::SaveGuts (FILE* f)
00620 {
00621 return gStringGeneric::SaveGuts( f );
00622 }
00623
00624 bool gString::RestoreGuts (FILE* f)
00625 {
00626 return gStringGeneric::RestoreGuts( f );
00627 }
00628
00629 void gString::Show (bool doShowAll)
00630 {
00631 printf("%s%s%s",
00632 doShowAll?"\"":"\0",
00633 str==NULL?"\0":(char*)str,
00634 doShowAll?"\"":"\0");
00635 }
00636
00637
00638
00639 int gString::thisMatch (char* s1, char* s2, bool doIgnoreCase)
00640 {
00641 if ( s1==nil || s2==nil ) return -2;
00642 if ( doIgnoreCase==false ) {
00643 return strcmp(s1,s2);
00644 }
00645 gString uStr1( s1 );
00646 gString uStr2( s2 );
00647 uStr1.UpString();
00648 uStr2.UpString();
00649 return strcmp( uStr1.Str(), uStr2.Str() );
00650 }
00651
00652 unsigned gString::thisFind (char* s,
00653 char* sub,
00654 unsigned startPos,
00655 bool doIgnoreCase)
00656 {
00657 char* ptr;
00658
00659 if ( s==nil || sub==nil ) return 0;
00660 ASSERTION(startPos>=1,"startPos>=1");
00661
00662 ptr = strstr( s+startPos-1, sub );
00663
00664 if ( doIgnoreCase==false ) {
00665 if ( ptr!=0 ) return ptr-s+startPos;
00666 return 0;
00667 }
00668
00669 gString uStr( s );
00670 s = uStr.Str();
00671 gString uSub( sub );
00672 uStr.UpString();
00673 uSub.UpString();
00674
00675 ptr = strstr( s, uSub.Str() );
00676 if ( ptr!=0 ) return ptr-s+startPos;
00677 return 0;
00678 }
00679
00680 unsigned gString::thisFindAny (char* s,
00681 char* strAny,
00682 bool doIgnoreCase,
00683 unsigned& posAny)
00684 {
00685 t_uchar uChr, fChr;
00686 unsigned i, k, uLen, aLen;
00687 char* uStr;
00688 char* aStr;
00689
00690 posAny = 0;
00691 if ( s==nil || strAny==nil ) return 0;
00692 gString sAll( s );
00693 gString sAny( strAny );
00694 if ( doIgnoreCase ) {
00695 sAll.UpString();
00696 sAny.UpString();
00697 }
00698 uLen = sAll.Length();
00699 uStr = sAll.Str();
00700 aLen = sAny.Length();
00701 aStr = sAny.Str();
00702 for (k=0; k<uLen; k++) {
00703 uChr = (t_uchar)uStr[k];
00704 for (i=0; i<aLen; ) {
00705 fChr = (t_uchar)aStr[i];
00706 i++;
00707 if ( uChr==fChr ) {
00708 posAny = i;
00709 return k+1;
00710 }
00711 }
00712 }
00713 return 0;
00714 }
00715
00716 unsigned gString::thisFindFwd (char* s,
00717 char* sub,
00718 unsigned startPos,
00719 bool doIgnoreCase,
00720 unsigned& nOcc)
00721 {
00722 if ( s==nil || sub==nil ) return 0;
00723 ASSERTION(startPos==1,"startPos==1");
00724
00725 gString uStr( s );
00726 gString uSub( sub );
00727 if ( doIgnoreCase ) {
00728 uStr.UpString();
00729 uSub.UpString();
00730 }
00731 return thisFindFwdOcc( uStr.Str(), uSub.Str(), nOcc );
00732 }
00733
00734 unsigned gString::thisFindFwdOcc (char* s,
00735 char* sub,
00736 unsigned& nOcc)
00737 {
00738 unsigned i, k;
00739 unsigned posFirst=0;
00740 unsigned pos=0, aPos, endPos=Length();
00741
00742 nOcc = 0;
00743 for (i=1, k=0; i<=endPos; i++) {
00744 aPos = thisFind( s+k, sub, 1, false );
00745 if ( aPos>0 ) {
00746 pos = k + aPos;
00747 if ( posFirst==0 ) posFirst = pos;
00748 nOcc++;
00749 k = pos;
00750 i = k+1;
00751 }
00752 }
00753 return posFirst;
00754 }
00755
00756 unsigned gString::thisFindBack (char* s,
00757 char* sub,
00758 unsigned startPos,
00759 bool doIgnoreCase,
00760 unsigned& nOcc)
00761 {
00762 if ( s==nil || sub==nil ) return 0;
00763 ASSERTION(startPos==1,"startPos==1");
00764
00765 gString uStr( s );
00766 gString uSub( sub );
00767 if ( doIgnoreCase ) {
00768 uStr.UpString();
00769 uSub.UpString();
00770 }
00771 return thisFindBackOcc( uStr.Str(), uSub.Str(), nOcc );
00772 }
00773
00774 unsigned gString::thisFindBackOcc (char* s,
00775 char* sub,
00776 unsigned& nOcc)
00777 {
00778 unsigned i, k;
00779 unsigned pos=0, aPos, endPos=Length();
00780
00781 nOcc = 0;
00782 for (i=1, k=0; i<=endPos; i++) {
00783 aPos = thisFind( s+k, sub, 1, false );
00784
00785 if ( aPos==0 ) return pos;
00786 pos = k + aPos;
00787 nOcc++;
00788 k = pos;
00789 i = k;
00790 }
00791
00792 return pos;
00793 }
00794
00795 unsigned gString::thisFindExcept (char* s,
00796 char* exceptStr,
00797 unsigned startPos,
00798 bool doIgnoreCase)
00799 {
00800
00801
00802 bool didFound;
00803 unsigned i=0, k, excLen=strlen(exceptStr);
00804 char c;
00805
00806 gString sUp( s );
00807 if ( doIgnoreCase ) sUp.UpString();
00808 char* sAll = sUp.Str();
00809
00810 ASSERTION(startPos==1,"startPos==1");
00811
00812 for (i=startPos-1; (c = sAll[i])!=0; ) {
00813 didFound = false;
00814 for (k=0; k<excLen && didFound==false; k++) {
00815 didFound = c==exceptStr[k];
00816 }
00817 i++;
00818 if ( didFound==false ) return i;
00819 }
00820 return 0;
00821 }
00822
00823