00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include <ncbi_pch.hpp>
00035 #include <corelib/ncbistd.hpp>
00036 #include <corelib/ncbistre.hpp>
00037 #include <corelib/stream_utils.hpp>
00038 #if defined(NCBI_OS_UNIX)
00039 # include <unistd.h>
00040 #endif
00041
00042
00043 BEGIN_NCBI_SCOPE
00044
00045
00046 CNcbiIstream& NcbiGetline(CNcbiIstream& is, string& str, const string& delims)
00047 {
00048 CT_INT_TYPE ch;
00049 char buf[1024];
00050 SIZE_TYPE pos = 0;
00051
00052 str.erase();
00053
00054 IOS_BASE::fmtflags f = is.flags();
00055 is.unsetf(IOS_BASE::skipws);
00056 #ifdef NO_PUBSYNC
00057 if ( !is.ipfx(1) ) {
00058 is.flags(f);
00059 return is;
00060 }
00061 #else
00062 CNcbiIstream::sentry s(is);
00063 if ( !s ) {
00064 is.clear(NcbiFailbit | is.rdstate());
00065 is.flags(f);
00066 return is;
00067 }
00068 #endif
00069
00070 SIZE_TYPE end = str.max_size();
00071 SIZE_TYPE i = 0;
00072 for (ch = is.rdbuf()->sbumpc(); !CT_EQ_INT_TYPE(ch, CT_EOF);
00073 ch = is.rdbuf()->sbumpc()) {
00074 i++;
00075 SIZE_TYPE delim_pos = delims.find(CT_TO_CHAR_TYPE(ch));
00076 if (delim_pos != NPOS) {
00077
00078
00079
00080
00081 CT_INT_TYPE next = is.rdbuf()->sgetc();
00082 if (!CT_EQ_INT_TYPE(next, CT_EOF)
00083 && delims.find(CT_TO_CHAR_TYPE(next), delim_pos + 1) != NPOS){
00084 is.rdbuf()->sbumpc();
00085 }
00086 break;
00087 }
00088 if (i == end) {
00089 is.clear(NcbiFailbit | is.rdstate());
00090 break;
00091 }
00092
00093 buf[pos++] = CT_TO_CHAR_TYPE(ch);
00094 if (pos == sizeof(buf)) {
00095 str.append(buf, pos);
00096 pos = 0;
00097 }
00098 }
00099 str.append(buf, pos);
00100 if (CT_EQ_INT_TYPE(ch, EOF))
00101 is.clear(NcbiEofbit | is.rdstate());
00102 if ( !i )
00103 is.clear(NcbiFailbit | is.rdstate());
00104
00105 #ifdef NO_PUBSYNC
00106 is.isfx();
00107 #endif
00108
00109 is.flags(f);
00110 return is;
00111 }
00112
00113
00114 #ifdef NCBI_COMPILER_GCC
00115 # if NCBI_COMPILER_VERSION < 300
00116 # define NCBI_COMPILER_GCC29x
00117 # endif
00118 #endif
00119
00120 extern CNcbiIstream& NcbiGetline(CNcbiIstream& is, string& str, char delim)
00121 {
00122 #if defined(NCBI_USE_OLD_IOSTREAM)
00123 return NcbiGetline(is, str, string(1, delim));
00124 #elif defined(NCBI_COMPILER_GCC29x)
00125
00126
00127
00128 return getline(is, str, delim);
00129 #else
00130 char buf[1024];
00131 str.erase();
00132 while (is.good()) {
00133 CT_INT_TYPE nextc = is.get();
00134 if (CT_EQ_INT_TYPE(nextc, CT_EOF)
00135 || CT_EQ_INT_TYPE(nextc, CT_TO_INT_TYPE(delim))) {
00136 break;
00137 }
00138 is.putback(nextc);
00139 is.get(buf, sizeof(buf), delim);
00140 str.append(buf, is.gcount());
00141 }
00142 if (str.empty() && is.eof()) {
00143 is.setstate(NcbiFailbit);
00144 }
00145 return is;
00146 #endif
00147 }
00148
00149
00150
00151 const char* Endl(void)
00152 {
00153 #if defined(NCBI_OS_MSWIN)
00154 static const char s_Endl[] = "\r\n";
00155 #else
00156 static const char s_Endl[] = "\n";
00157 #endif
00158 return s_Endl;
00159 }
00160
00161
00162
00163 CNcbiIstream& NcbiGetlineEOL(CNcbiIstream& is, string& str)
00164 {
00165 #if defined(NCBI_OS_MSWIN)
00166 NcbiGetline(is, str, '\n');
00167 if (!str.empty() && str[str.length()-1] == '\r')
00168 str.resize(str.length() - 1);
00169 #elif defined(NCBI_OS_DARWIN)
00170 NcbiGetline(is, str, "\r\n");
00171 #else
00172 NcbiGetline(is, str, '\n');
00173 #endif
00174
00175 if (is.fail() && !is.eof() && !is.gcount() && str.empty())
00176 is.clear(is.rdstate() & ~NcbiFailbit);
00177 return is;
00178 }
00179
00180
00181 bool NcbiStreamCopy(CNcbiOstream& os, CNcbiIstream& is)
00182 {
00183 if (!is.good())
00184 return false;
00185 #ifndef NCBI_COMPILER_GCC
00186 os << is.rdbuf();
00187 #elif NCBI_COMPILER_VERSION <= 330
00188
00189
00190
00191
00192
00193 do {
00194 char buf[4096];
00195 is.read(buf, sizeof(buf));
00196 streamsize count = is.gcount();
00197 if (!count)
00198 break;
00199 if (!os.write(buf, count))
00200 break;
00201 } while (is.good());
00202 #else
00203 os << is.rdbuf();
00204 #endif
00205 if (!os.good())
00206 return false;
00207 os.flush();
00208 if (!os.good())
00209 return false;
00210 if (!CT_EQ_INT_TYPE(is.peek(), CT_EOF)) {
00211 os.clear(NcbiFailbit);
00212 return false;
00213 }
00214 return true;
00215 }
00216
00217
00218 CNcbiOstrstreamToString::operator string(void) const
00219 {
00220 SIZE_TYPE length = m_Out.pcount();
00221 if ( length == 0 )
00222 return string();
00223 const char* str = m_Out.str();
00224 m_Out.freeze(false);
00225 return string(str, length);
00226 }
00227
00228
00229 CNcbiOstream& operator<<(CNcbiOstream& out, const CTempString& str)
00230 {
00231 return out.write(str.data(), str.length());
00232 }
00233
00234
00235 CNcbiOstream& operator<<(CNcbiOstream& out, CUpcaseStringConverter s)
00236 {
00237 ITERATE ( string, c, s.m_String ) {
00238 out.put(char(toupper((unsigned char)(*c))));
00239 }
00240 return out;
00241 }
00242
00243
00244 CNcbiOstream& operator<<(CNcbiOstream& out, CLocaseStringConverter s)
00245 {
00246 ITERATE ( string, c, s.m_String ) {
00247 out.put(char(tolower((unsigned char)(*c))));
00248 }
00249 return out;
00250 }
00251
00252
00253 CNcbiOstream& operator<<(CNcbiOstream& out, CUpcaseCharPtrConverter s)
00254 {
00255 for ( const char* c = s.m_String; *c; ++c ) {
00256 out.put(char(toupper((unsigned char)(*c))));
00257 }
00258 return out;
00259 }
00260
00261
00262 CNcbiOstream& operator<<(CNcbiOstream& out, CLocaseCharPtrConverter s)
00263 {
00264 for ( const char* c = s.m_String; *c; ++c ) {
00265 out.put(char(tolower((unsigned char)(*c))));
00266 }
00267 return out;
00268 }
00269
00270
00271 #ifdef NCBI_COMPILER_MSVC
00272 # if _MSC_VER >= 1200 && _MSC_VER < 1300
00273 CNcbiOstream& operator<<(CNcbiOstream& out, __int64 val)
00274 {
00275 return (out << NStr::Int8ToString(val));
00276 }
00277 # endif
00278 #endif
00279
00280
00281 static const char s_Hex[] = "0123456789ABCDEF";
00282
00283 string Printable(char c)
00284 {
00285 string s;
00286 switch ( c ) {
00287 case '\0': s = "\\0"; break;
00288 case '\\': s = "\\\\"; break;
00289 case '\n': s = "\\n"; break;
00290 case '\t': s = "\\t"; break;
00291 case '\r': s = "\\r"; break;
00292 case '\v': s = "\\v"; break;
00293 default:
00294 {
00295 if ( isprint((unsigned char) c) ) {
00296 s = c;
00297 } else {
00298 s = "\\x";
00299 s += s_Hex[(unsigned char) c / 16];
00300 s += s_Hex[(unsigned char) c % 16];
00301 }
00302 }
00303 }
00304 return s;
00305 }
00306
00307
00308 static inline
00309 bool s_IsQuoted(char c)
00310 {
00311 return (c == '\t' || c == '\v' || c == '\b' ||
00312 c == '\r' || c == '\f' || c == '\a' ||
00313 c == '\n' || c == '\\' || c == '\'' ||
00314 c == '"' || !isprint((unsigned char) c) ? true : false);
00315 }
00316
00317
00318 static inline
00319 void s_WritePrintable(CNcbiOstream& out, char c, char n)
00320 {
00321 switch ( c ) {
00322 case '\t': out.write("\\t", 2); return;
00323 case '\v': out.write("\\v", 2); return;
00324 case '\b': out.write("\\b", 2); return;
00325 case '\r': out.write("\\r", 2); return;
00326 case '\f': out.write("\\f", 2); return;
00327 case '\a': out.write("\\a", 2); return;
00328 case '\n': out.write("\\n", 2); return;
00329 case '\\': out.write("\\\\", 2); return;
00330 case '\'': out.write("\\'", 2); return;
00331 case '"': out.write("\\\"", 2); return;
00332 default:
00333 if ( isprint((unsigned char) c) ) {
00334 out.put(c);
00335 return;
00336 }
00337 break;
00338 }
00339
00340 bool full = !s_IsQuoted(n) && n >= '0' && n <= '7' ? true : false;
00341 unsigned char v;
00342 char octal[4];
00343 int k = 1;
00344
00345 *octal = '\\';
00346 v = (unsigned char) c >> 6;
00347 if (v || full) {
00348 octal[k++] = '0' + v;
00349 full = true;
00350 }
00351 v = ((unsigned char) c >> 3) & 7;
00352 if (v || full) {
00353 octal[k++] = '0' + v;
00354 }
00355 v = (unsigned char) c & 7;
00356 octal [k++] = '0' + v;
00357 out.write(octal, k);
00358 }
00359
00360
00361 CNcbiOstream& operator<<(CNcbiOstream& out, CPrintableStringConverter s)
00362 {
00363 size_t size = s.m_String.size();
00364 if (size) {
00365 const char* data = s.m_String.data();
00366 for (size_t i = 0; i < size - 1; ++i) {
00367 s_WritePrintable(out, data[i], data[i + 1]);
00368 }
00369 s_WritePrintable(out, data[size - 1], '\0');
00370 }
00371 return out;
00372 }
00373
00374
00375 CNcbiOstream& operator<<(CNcbiOstream& out, CPrintableCharPtrConverter s)
00376 {
00377 const char* p = s.m_String;
00378 char c = *p;
00379 while (c) {
00380 char n = *++p;
00381 s_WritePrintable(out, c, n);
00382 c = n;
00383 }
00384 return out;
00385 }
00386
00387
00388 #if defined(NCBI_COMPILER_WORKSHOP)
00389
00390 # if (NCBI_COMPILER_VERSION == 530)
00391
00392
00393 template<>
00394 istream& istream::read(char *s, streamsize n)
00395 {
00396 sentry ipfx(*this, 1);
00397
00398 try {
00399 if (rdbuf()->sgetc() == traits_type::eof()) {
00400
00401 __chcount = 0;
00402 setstate(eofbit);
00403 return *this;
00404 }
00405 __chcount = rdbuf()->sgetn(s, n);
00406 if (__chcount == 0) {
00407 setstate(eofbit);
00408 } else if (__chcount < n) {
00409 setstate(eofbit | failbit);
00410 } else if (!ipfx) {
00411 setstate(failbit);
00412 }
00413 } catch (...) {
00414 setstate(failbit);
00415 throw;
00416 }
00417
00418 return *this;
00419 }
00420 # endif
00421 #endif
00422
00423
00424 EEncodingForm ReadIntoUtf8(
00425 CNcbiIstream& input,
00426 CStringUTF8* result,
00427 EEncodingForm ef ,
00428 EReadUnknownNoBOM what_if_no_bom
00429 )
00430 {
00431 EEncodingForm ef_bom = eEncodingForm_Unknown;
00432 result->erase();
00433 if (!input.good()) {
00434 return ef_bom;
00435 }
00436
00437 const int buf_size = 4096;
00438 char tmp[buf_size+2];
00439 Uint2* us = reinterpret_cast<Uint2*>(tmp);
00440
00441
00442 const int bom_max = 4;
00443 memset(tmp,0,bom_max);
00444 input.read(tmp,bom_max);
00445 int n = input.gcount();
00446 {
00447 int bom_len=0;
00448 Uchar* uc = reinterpret_cast<Uchar*>(tmp);
00449 if (n >= 3 && uc[0] == 0xEF && uc[1] == 0xBB && uc[2] == 0xBF) {
00450 ef_bom = eEncodingForm_Utf8;
00451 uc[0] = uc[3];
00452 bom_len=3;
00453 }
00454 else if (n >= 2 && (us[0] == 0xFEFF || us[0] == 0xFFFE)) {
00455 if (us[0] == 0xFEFF) {
00456 ef_bom = eEncodingForm_Utf16Native;
00457 } else {
00458 ef_bom = eEncodingForm_Utf16Foreign;
00459 }
00460 us[0] = us[1];
00461 bom_len=2;
00462 }
00463 if (ef == eEncodingForm_Unknown || ef == ef_bom) {
00464 ef = ef_bom;
00465 n -= bom_len;
00466 }
00467
00468 }
00469
00470
00471 while (n != 0 || (input.good() && !input.eof())) {
00472
00473 if (n == 0) {
00474 input.read(tmp, buf_size);
00475 n = input.gcount();
00476 result->reserve(max(result->capacity(), result->size() + n));
00477 }
00478 tmp[n] = '\0';
00479
00480 switch (ef) {
00481 case eEncodingForm_Utf16Foreign:
00482 {
00483 char buf[buf_size];
00484 swab(tmp,buf,n);
00485 memcpy(tmp, buf, n);
00486 }
00487
00488 case eEncodingForm_Utf16Native:
00489 {
00490 Uint2* u = us;
00491 for (n = n/2; n--; ++u) {
00492 result->Append(*u);
00493 }
00494 }
00495 break;
00496 case eEncodingForm_ISO8859_1:
00497 result->Append(tmp,eEncoding_ISO8859_1);
00498 break;
00499 case eEncodingForm_Windows_1252:
00500 result->Append(tmp,eEncoding_Windows_1252);
00501 break;
00502 case eEncodingForm_Utf8:
00503
00504 result->append(tmp,n);
00505 break;
00506 default:
00507 if (what_if_no_bom == eNoBOM_GuessEncoding) {
00508 if (n == bom_max) {
00509 input.read(tmp + n, buf_size - n);
00510 n += input.gcount();
00511 result->reserve(max(result->capacity(), result->size() + n));
00512 }
00513 tmp[n] = '\0';
00514 EEncoding enc = CStringUTF8::GuessEncoding(tmp);
00515 switch (enc) {
00516 default:
00517 case eEncoding_Unknown:
00518 if (CStringUTF8::GetValidBytesCount(tmp, n) != 0) {
00519 ef = eEncodingForm_Utf8;
00520 result->Append(tmp,enc);
00521 }
00522 else {
00523 NCBI_THROW(CCoreException, eCore,
00524 "ReadIntoUtf8: cannot guess text encoding");
00525 }
00526 break;
00527 case eEncoding_UTF8:
00528 ef = eEncodingForm_Utf8;
00529
00530 case eEncoding_Ascii:
00531 case eEncoding_ISO8859_1:
00532 case eEncoding_Windows_1252:
00533 result->Append(tmp,enc);
00534 break;
00535 }
00536 } else {
00537
00538 result->append(tmp,n);
00539 }
00540 break;
00541 }
00542 n = 0;
00543 }
00544 return ef_bom;
00545 }
00546
00547
00548 EEncodingForm GetTextEncodingForm(CNcbiIstream& input,
00549 EBOMDiscard discard_bom)
00550 {
00551 EEncodingForm ef = eEncodingForm_Unknown;
00552 if (input.good()) {
00553 const int bom_max = 4;
00554 char tmp[bom_max];
00555 memset(tmp,0,bom_max);
00556 Uint2* us = reinterpret_cast<Uint2*>(tmp);
00557 Uchar* uc = reinterpret_cast<Uchar*>(tmp);
00558 input.get(tmp[0]);
00559 int n = input.gcount();
00560 if (n == 1 && (uc[0] == 0xEF || uc[0] == 0xFE || uc[0] == 0xFF)) {
00561 input.get(tmp[1]);
00562 if (input.gcount()==1) {
00563 ++n;
00564 if (us[0] == 0xFEFF) {
00565 ef = eEncodingForm_Utf16Native;
00566 } else if (us[0] == 0xFFFE) {
00567 ef = eEncodingForm_Utf16Foreign;
00568 } else if (uc[1] == 0xBB) {
00569 input.get(tmp[2]);
00570 if (input.gcount()==1) {
00571 ++n;
00572 if (uc[2] == 0xBF) {
00573 ef = eEncodingForm_Utf8;
00574 }
00575 }
00576 }
00577 }
00578 }
00579 if (ef == eEncodingForm_Unknown) {
00580 if (n > 1) {
00581 CStreamUtils::Pushback(input,tmp,n);
00582 } else if (n == 1) {
00583 input.unget();
00584 }
00585 } else {
00586 if (discard_bom == eBOM_Keep) {
00587 CStreamUtils::Pushback(input,tmp,n);
00588 }
00589 }
00590 }
00591 return ef;
00592 }
00593
00594
00595
00596
00597
00598
00599 extern "C" void BASE64_Encode
00600 (const void* src_buf,
00601 size_t src_size,
00602 size_t* src_read,
00603 void* dst_buf,
00604 size_t dst_size,
00605 size_t* dst_written,
00606 size_t* line_len)
00607 {
00608 static const char syms[] =
00609 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00610 "abcdefghijklmnopqrstuvwxyz"
00611 "0123456789+/";
00612 const size_t max_len = line_len ? *line_len : 76;
00613 const size_t max_src =
00614 ((dst_size - (max_len ? dst_size/(max_len + 1) : 0)) >> 2) * 3;
00615 unsigned char* src = (unsigned char*) src_buf;
00616 unsigned char* dst = (unsigned char*) dst_buf;
00617 size_t len = 0, i = 0, j = 0;
00618 unsigned char temp = 0, c;
00619 unsigned char shift = 2;
00620 if (!max_src || !src_size) {
00621 *src_read = 0;
00622 *dst_written = 0;
00623 if (dst_size > 0) {
00624 *dst = '\0';
00625 }
00626 return;
00627 }
00628 if (src_size > max_src) {
00629 src_size = max_src;
00630 }
00631 c = src[0];
00632 for (;;) {
00633 unsigned char bits = (c >> shift) & 0x3F;
00634 if (max_len && len >= max_len) {
00635 dst[j++] = '\n';
00636 len = 0;
00637 }
00638 _ASSERT((size_t)(temp | bits) < sizeof(syms) - 1);
00639 dst[j++] = syms[temp | bits];
00640 len++;
00641 if (i >= src_size) {
00642 break;
00643 }
00644 shift += 2;
00645 shift &= 7;
00646 temp = (c << (8 - shift)) & 0x3F;
00647 if (shift) {
00648 c = ++i < src_size ? src[i] : 0;
00649 } else if (i + 1 == src_size) {
00650 i++;
00651 }
00652 }
00653 _ASSERT(j <= dst_size);
00654 *src_read = i;
00655 for (i = 0; i < (3 - src_size % 3) % 3; i++) {
00656 if (max_len && len >= max_len) {
00657 dst[j++] = '\n';
00658 len = 0;
00659 }
00660 dst[j++] = '=';
00661 len++;
00662 }
00663 _ASSERT(j <= dst_size);
00664 *dst_written = j;
00665 if (j < dst_size) {
00666 dst[j] = '\0';
00667 }
00668 }
00669
00670
00671 extern "C" int BASE64_Decode
00672 (const void* src_buf,
00673 size_t src_size,
00674 size_t* src_read,
00675 void* dst_buf,
00676 size_t dst_size,
00677 size_t* dst_written)
00678 {
00679 unsigned char* src = (unsigned char*) src_buf;
00680 unsigned char* dst = (unsigned char*) dst_buf;
00681 size_t i = 0, j = 0, k = 0, l;
00682 unsigned int temp = 0;
00683 if (src_size < 4 || dst_size < 3) {
00684 *src_read = 0;
00685 *dst_written = 0;
00686 return 0;
00687 }
00688 for (;;) {
00689 int ok = i < src_size ? 1 : 0;
00690 unsigned char c = ok ? src[i++] : '=';
00691 if (c == '=') {
00692 c = 64;
00693 } else if (c >= 'A' && c <= 'Z') {
00694 c -= 'A';
00695 } else if (c >= 'a' && c <= 'z') {
00696 c -= 'a' - 26;
00697 } else if (c >= '0' && c <= '9') {
00698 c -= '0' - 52;
00699 } else if (c == '+') {
00700 c = 62;
00701 } else if (c == '/') {
00702 c = 63;
00703 } else {
00704 continue;
00705 }
00706 temp <<= 6;
00707 temp |= c & 0x3F;
00708 if (!(++k & 3) || c == 64) {
00709 if (c == 64) {
00710 if (k < 2) {
00711 if (ok) {
00712
00713 --i;
00714 }
00715 break;
00716 }
00717 switch (k) {
00718 case 2:
00719 temp >>= 4;
00720 break;
00721 case 3:
00722 temp >>= 10;
00723 break;
00724 case 4:
00725 temp >>= 8;
00726 break;
00727 default:
00728 _ASSERT(0);
00729 break;
00730 }
00731 l = 4 - k;
00732 while (l > 0) {
00733
00734 if (i >= src_size)
00735 break;
00736 if (src[i] == '=')
00737 l--;
00738 else if (src[i] != '\r' && src[i] != '\n')
00739 break;
00740 i++;
00741 }
00742 } else {
00743 k = 0;
00744 }
00745 switch (k) {
00746 case 0:
00747 dst[j++] = (temp & 0xFF0000) >> 16;
00748 ;
00749 case 4:
00750 dst[j++] = (temp & 0xFF00) >> 8;
00751
00752 case 3:
00753 dst[j++] = (temp & 0xFF);
00754 break;
00755 default:
00756 break;
00757 }
00758 if (j + 3 >= dst_size || c == 64) {
00759 break;
00760 }
00761 temp = 0;
00762 }
00763 }
00764 *src_read = i;
00765 *dst_written = j;
00766 return i && j ? 1 : 0;
00767 }
00768
00769
00770 END_NCBI_SCOPE
00771
00772
00773
00774
00775 #if defined(NCBI_USE_OLD_IOSTREAM)
00776 extern NCBI_NS_NCBI::CNcbiOstream& operator<<(NCBI_NS_NCBI::CNcbiOstream& os,
00777 const NCBI_NS_STD::string& str)
00778 {
00779 return str.empty() ? os : os << str.c_str();
00780 }
00781
00782
00783 extern NCBI_NS_NCBI::CNcbiIstream& operator>>(NCBI_NS_NCBI::CNcbiIstream& is,
00784 NCBI_NS_STD::string& str)
00785 {
00786 int ch;
00787 if ( !is.ipfx() )
00788 return is;
00789
00790 str.erase();
00791
00792 SIZE_TYPE end = str.max_size();
00793 if ( is.width() )
00794 end = (int)end < is.width() ? end : is.width();
00795
00796 SIZE_TYPE i = 0;
00797 for (ch = is.rdbuf()->sbumpc();
00798 ch != EOF && !isspace((unsigned char) ch);
00799 ch = is.rdbuf()->sbumpc()) {
00800 str.append(1, (char)ch);
00801 i++;
00802 if (i == end)
00803 break;
00804 }
00805 if (ch == EOF)
00806 is.clear(NcbiEofbit | is.rdstate());
00807 if ( !i )
00808 is.clear(NcbiFailbit | is.rdstate());
00809
00810 is.width(0);
00811 return is;
00812 }
00813
00814
00815 #endif
00816
00817