size_t wcrtomb (char *dst, wchar_t wc, mbstate_t * __UNUSED_PARAM (ps)) { char byte_bucket [MB_LEN_MAX]; char* tmp_dst = dst ? dst : byte_bucket; return (size_t)__wcrtomb_cp (tmp_dst, wc, get_codepage (), MB_CUR_MAX); }
/* Return just the first byte after translating to multibyte. */ int wctob (wint_t wc ) { wchar_t w = wc; char c; int invalid_char = 0; if (!WideCharToMultiByte (get_codepage(), 0 /* Is this correct flag? */, &w, 1, &c, 1, NULL, &invalid_char) || invalid_char) return EOF; return (int) c; }
wint_t btowc (int c) { if (c == EOF) return (WEOF); else { unsigned char ch = c; wchar_t wc = WEOF; MultiByteToWideChar (get_codepage(), MB_ERR_INVALID_CHARS, (char*)&ch, 1, &wc, 1); return wc; } }
size_t wcsrtombs (char *dst, const wchar_t **src, size_t len, mbstate_t * __UNUSED_PARAM (ps)) { int ret = 0; size_t n = 0; const unsigned int cp = get_codepage(); const unsigned int mb_max = MB_CUR_MAX; const wchar_t *pwc = *src; if (src == NULL || *src == NULL) /* undefined behavior */ return 0; if (dst != NULL) { while (n < len) { if ((ret = __wcrtomb_cp (dst, *pwc, cp, mb_max)) <= 0) return (size_t) -1; n += ret; dst += ret; if (*(dst - 1) == '\0') { *src = (wchar_t*) NULL;; return (n - 1); } pwc++; } *src = pwc; } else { char byte_bucket [MB_LEN_MAX]; while (n < len) { if ((ret = __wcrtomb_cp (byte_bucket, *pwc, cp, mb_max)) <= 0) return (size_t) -1; n += ret; if (byte_bucket [ret - 1] == '\0') return (n - 1); pwc++; } } return n; }
BOOL print_registry_value(struct cache_entry *ce) { struct find_reg *fr; struct _finddatai64_t fd; wchar_t wszName[FILENAME_MAX]; DWORD dwSize=0; DWORD dwType; DWORD dwGotType=0; LONG lErrCode; typedef LONG (WINAPI *PFNREGQUERYREFLECTIONKEY)( HKEY hBase, BOOL *pbIsReflectionDisabled // BUG in MSDN doc: BOOL ); static PFNREGQUERYREFLECTIONKEY pfnRegQueryReflectionKey; fr = (struct find_reg*)xmalloc(sizeof(*fr)); memset(fr, 0, sizeof(*fr)); fr->fr_magic = FR_MAGIC; if (ce->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { dwType = DT_DIR; } else { dwType = DT_REG; } if (!_PrepReg(ce->ce_abspath, fr, dwType)) { _RegFindClose((long)fr); return FALSE; } if (!_LookupReg(fr, &fd)) { _RegFindClose((long)fr); return FALSE; } if (dwType == DT_DIR) { // // Reg key: show the class name if any. // if (fr->fr_szClass != NULL) { more_printf(" Class Name: \"%s\"\n", fr->fr_szClass); } // // Reg key: report if 64-bit <-> 32-bit reflection is enabled. // // The reflection is between HKLM\Software and // HKLM\Software\Wow6432Node. // if (gbIsWindowsWOW64) { if (DynaLoad("ADVAPI32.DLL", "RegQueryReflectionKey", (PPFN)&pfnRegQueryReflectionKey)) { // // BUG: Documented as bReflectionEnabled but really // is bReflectionDisabled! // // Note: Always FALSE if !gbIsWindowsWOW64 // // See http://www.codeproject.com/system/Reflection.asp // BOOL bReflectionDisabled = FALSE; // BUG: Reversed if ((*pfnRegQueryReflectionKey)(fr->fr_hKey, &bReflectionDisabled) == ERROR_SUCCESS) { if (!IsWindows7) { // Windows 7 disables all reflection always if (bReflectionDisabled) { // BUG: Reversed more_printf(" Reflection is disabled.\n"); } } } } } _RegFindClose((long)fr); return TRUE; } // // Show the reg value // if (fr->fr_szValue == NULL) { _RegFindClose((long)fr); return FALSE; } // // Query a single value - just check existance and get size // // BUG: Must use Unicode as the RegA functions wrongly // use the Ansi code page. if (::MultiByteToWideChar(get_codepage(), 0, fr->fr_szValue, -1, wszName, FILENAME_MAX) == 0) { _RegFindClose((long)fr); return FALSE; } if (wcscmp(wszName, L"[default]") == 0) { wszName[0] = L'\0'; } dwSize = (DWORD)(fd.size); dwSize = (dwSize + 10) * sizeof(WCHAR); BOOL bUnicode = TRUE; PBYTE pbData = (PBYTE) alloca(dwSize); memset(pbData, 0, dwSize); // // Note: RegQueryValueExW will fail on Win9x // if ((lErrCode = ::RegQueryValueExW(fr->fr_hKey, wszName, 0, &dwGotType, pbData, &dwSize)) != ERROR_SUCCESS) { if (lErrCode != ERROR_NOT_SUPPORTED && lErrCode != ERROR_CALL_NOT_IMPLEMENTED) { ::SetLastError((DWORD)lErrCode); _RegFindClose((long)fr); return FALSE; } // // Win9x: Try again using the ANSI version. // Note that this will wrongly convert OEM chars. // if ((lErrCode = ::RegQueryValueExA(fr->fr_hKey, fr->fr_szValue, 0, &dwGotType, pbData, &dwSize)) != ERROR_SUCCESS) { ::SetLastError((DWORD)lErrCode); _RegFindClose((long)fr); return FALSE; } bUnicode = FALSE; } BOOL bExpandedMui = FALSE; if (IsVista && gbExpandMui) { if ((dwGotType == REG_SZ || dwGotType == REG_EXPAND_SZ || dwGotType == REG_MULTI_SZ) && dwSize >= sizeof(WCHAR) && ((LPCWSTR)pbData)[0] == L'@' && DynaLoad("ADVAPI32.DLL", "RegLoadMUIStringW", (PPFN)&pfnRegLoadMUIStringW)) { PBYTE pbMuiData = NULL; // // Get the required buffer size for the expanded MUI string // DWORD dwMuiLen = 0; // byte len WCHAR wszDummy[4]; // dummy buffer wszDummy[0] = L'\0'; if ((lErrCode = (*pfnRegLoadMUIStringW)(fr->fr_hKey, wszName, wszDummy, 0, &dwMuiLen, 0, NULL)) != ERROR_MORE_DATA) { goto NoMui; // Use the @-string as-is } pbMuiData = (PBYTE) alloca(dwMuiLen+8); // // Now read the data for real // // dwMuiLen will be overwritten with the actual length // if ((lErrCode = (*pfnRegLoadMUIStringW)(fr->fr_hKey, wszName, (LPWSTR)pbMuiData, dwMuiLen, &dwMuiLen, 0, NULL)) != ERROR_SUCCESS) { goto NoMui; // Use the @-string as-is } // // Replace pbData with the expanded MUI data // pbData = pbMuiData; dwSize = dwMuiLen; bExpandedMui = TRUE; } } NoMui: fd.size = dwSize; _RegFindClose((long)fr); switch (dwGotType) { case REG_SZ: case REG_EXPAND_SZ: { DWORD dwLen, dwStrLen=0; LPSTR szBuf; more_fputs((dwGotType == REG_SZ ? " REG_SZ=\"" : " REG_EXPAND_SZ=\""), stdmore); if (!bUnicode) { more_fputs((LPCSTR)pbData, stdmore); dwLen = dwSize; dwStrLen = strlen((LPCSTR)pbData) + 1; } else { dwLen = dwSize+10; szBuf = (LPSTR)alloca(dwLen); if ((dwLen = ::WideCharToMultiByte(get_codepage(), 0, (LPCWSTR)pbData, dwSize/2, szBuf, dwLen, NULL, NULL)) == 0) { more_fputs("???", stdmore); } else { more_fputs(szBuf, stdmore); dwStrLen = strlen(szBuf) + 1; } } more_fputs("\"\n", stdmore); if (dwLen != dwStrLen) { // // Warn if the string length does not match the registry // buffer size. Could indicate hidden data after \0 // more_printf(" *** String length with \\0 (%u) <> registry length (%u) ***\n", dwStrLen, dwLen); } break; } case REG_NONE: case REG_BINARY: case REG_LINK: case REG_MULTI_SZ: case REG_RESOURCE_LIST: case REG_FULL_RESOURCE_DESCRIPTOR: case REG_RESOURCE_REQUIREMENTS_LIST: default: { DWORD dwBytes, i; char ch; switch (dwGotType) { case REG_NONE: more_printf(" REG_NONE (%u bytes)\n", dwSize); break; case REG_BINARY: more_printf(" REG_BINARY (%u bytes)\n", dwSize); break; case REG_LINK: more_printf(" REG_LINK (%u bytes)\n", dwSize); break; case REG_MULTI_SZ: more_printf(" REG_MULTI_SZ (%u bytes)\n", dwSize); break; case REG_RESOURCE_LIST: more_printf(" REG_RESOURCE_LIST (%u bytes)\n", dwSize); break; case REG_FULL_RESOURCE_DESCRIPTOR: more_printf(" REG_FULL_RESOURCE_DESCRIPTOR (%u bytes)\n", dwSize); break; case REG_RESOURCE_REQUIREMENTS_LIST: more_printf(" REG_RESOURCE_REQUIREMENTS_LIST (%u bytes)\n", dwSize); break; default: more_printf(" Unknown type (%u), size %u bytes.\n", dwGotType, dwSize); break; } for (unsigned long l=0; l < dwSize; l += 16) { more_printf("%8x: ", l); dwBytes = ((dwSize - l < 16) ? (dwSize - l) : 16); for (i=0; i < dwBytes; ++i) { more_printf("%02.2x ", pbData[l+i]); } for (; i < 16; ++i) { more_fputs(" ", stdmore); } more_fputs(" ", stdmore); for (i=0; i < dwBytes; ++i) { if ((ch = pbData[l+i]) < 040 || ch > 0176) { ch = '.'; } more_putc(ch, stdmore); } for (; i < 16; ++i) { more_putc(' ', stdmore); } more_putc('\n', stdmore); } break; } case REG_DWORD: more_printf(" REG_DWORD=%u (0x%08X)\n", *(PDWORD)pbData, *(PDWORD)pbData); if (dwSize != sizeof(DWORD)) { more_printf(" *** DWORD length (%u) is not standard. ***\n", dwSize); } break; case REG_QWORD: more_printf(" REG_QWORD=%I64u (0x%I64X)\n", *(unsigned __int64 *)pbData, *(unsigned __int64 *)pbData); if (dwSize != sizeof(unsigned __int64)) { more_printf(" *** QWORD length (%u) is not standard. ***\n", dwSize); } break; } if (bExpandedMui) { more_printf(" Expanded MUI string.\n"); } return TRUE; }
// // Query a symbolic registry link // // Also do --regsetval and --regdelval // char * _GetRegistryLink(struct cache_entry *ce, char *szPath) { struct find_reg *fr; if ((ce->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { return NULL; // regval } fr = (struct find_reg*)xmalloc(sizeof(*fr)); memset(fr, 0, sizeof(*fr)); fr->fr_magic = FR_MAGIC; DWORD dwType = DT_DIR; if (!_PrepReg(ce->ce_abspath, fr, dwType) || fr->fr_szKey == NULL) { _RegFindClose((long)fr); return NULL; } wchar_t wszPath[FILENAME_MAX+1]; // // Convert the registry path to Unicode // if (MultiByteToWideChar(get_codepage(), 0, fr->fr_szKey, -1, wszPath, FILENAME_MAX) == 0) { _RegFindClose((long)fr); return NULL; } LONG lErrCode; DWORD dwDisposition = 0; HKEY hkLink = NULL; DWORD dwAccess = KEY_READ; if (gbRegDelVal || gbRegSetVal) { dwAccess |= KEY_WRITE; } if (gbIsWindowsWOW64 && !gb32bit) { // Allow access to 64 bit keys dwAccess |= 0x0100; // KEY_WOW64_64KEY } // // Must use RegCreateKeyExW to pass REG_OPTION_OPEN_LINK // lErrCode = RegCreateKeyExW(fr->fr_hRoot, wszPath, 0, NULL/*wszClass*/, REG_OPTION_OPEN_LINK, dwAccess, NULL, &hkLink, &dwDisposition); if (lErrCode != 0) { _RegFindClose((long)fr); return NULL; } dwType = 0; WCHAR wszData[FILENAME_MAX+1]; DWORD dwLen = FILENAME_MAX*sizeof(WCHAR); wszData[0] = L'\0'; lErrCode = RegQueryValueExW(hkLink, L"SymbolicLinkValue", NULL, &dwType, (PBYTE)wszData, &dwLen); if (gbRegDelVal) { // // --regdelval Delete a test value for exploring registry reflection // and registry redirection. // ::RegDeleteValueW(hkLink, L"TestValue"); } if (gbRegSetVal) { // // --regsetval Set a test value for exploring registry reflection // and registry redirection. // DWORD dwVal = 1; ::RegSetValueExW(hkLink, L"TestValue", 0, REG_DWORD, (PBYTE)&dwVal, sizeof(dwVal)); } RegCloseKey(hkLink); hkLink = NULL; // // Sanity check: If we somehow created a new key accidentally // via RegCreateKeyEx, bail immediately! // if (dwDisposition == REG_CREATED_NEW_KEY) { more_printf("Error: Created new key by mistake: %s\n", fr->fr_szKey); _RegFindClose((long)fr); exit(1); } _RegFindClose((long)fr); if (lErrCode != 0) { return NULL; } wszData[dwLen/2] = L'\0'; // BUG: Not NULL terminated // // Convert from wchar_t to multibyte string // if (!WideCharToMultiByte(get_codepage(), 0, wszData, -1, szPath, FILENAME_MAX-1, NULL, NULL)) { return NULL; } return szPath; }
bool IE_Imp_MSWrite::read_ffntb () { int pnFfntb, pnMac, fonts_count = 0, cbFfn, fflen; unsigned char buf[2], ffid; wri_font *fonts; char *ffn; UT_DEBUGMSG(("Fonts:\n")); pnFfntb = wri_struct_value(wri_file_header, "pnFfntb"); pnMac = wri_struct_value(wri_file_header, "pnMac"); // if pnFfntb is the same as pnMac, there are no fonts if (pnFfntb == pnMac) { UT_DEBUGMSG((" (none)\n")); return true; } if (gsf_input_seek(mFile, pnFfntb++ * 0x80, G_SEEK_SET)) { UT_WARNINGMSG(("read_ffntb: Can't seek FFNTB!\n")); return false; } // the first two bytes are the number of fonts if (!gsf_input_read(mFile, 2, buf)) { UT_WARNINGMSG(("read_ffntb: Can't read FFNTB!\n")); return false; } wri_fonts_count = READ_WORD(buf); UT_DEBUGMSG((" reported: %d\n", wri_fonts_count)); while (true) { if (!gsf_input_read(mFile, 2, buf)) { UT_WARNINGMSG(("read_ffntb: Can't read cbFfn!\n")); wri_fonts_count = fonts_count; free_ffntb(); return false; } cbFfn = READ_WORD(buf); if (cbFfn == 0) break; if (cbFfn == 0xffff) { if (gsf_input_seek(mFile, pnFfntb++ * 0x80, G_SEEK_SET)) { UT_WARNINGMSG(("read_ffntb: Can't seek next FFNTB!\n")); wri_fonts_count = fonts_count; free_ffntb(); return false; } continue; } // add one more font fonts = (wri_font *) realloc(wri_fonts, (fonts_count + 1) * sizeof(wri_font)); if (!fonts) { UT_WARNINGMSG(("read_ffntb: Out of memory!\n")); wri_fonts_count = fonts_count; free_ffntb(); return false; } wri_fonts = fonts; // This is the font family identifier. It can either be FF_DONTCARE, // FF_ROMAN, FF_SWISS, FF_MODERN, FF_SCRIPT or FF_DECORATIVE. These // are defined in <windows.h>, but I don't know what to do with them. if (!gsf_input_read(mFile, 1, &ffid)) { UT_WARNINGMSG(("read_ffntb: Can't read ffid!\n")); wri_fonts_count = fonts_count; free_ffntb(); return false; } wri_fonts[fonts_count].ffid = ffid; cbFfn--; // we've already read ffid ffn = static_cast<char *>(malloc(cbFfn)); if (!ffn) { UT_WARNINGMSG(("read_ffntb: Out of memory!\n")); wri_fonts_count = fonts_count; free_ffntb(); return false; } if (!gsf_input_read(mFile, cbFfn, (guint8 *) ffn)) { UT_WARNINGMSG(("read_ffntb: Can't read szFfn!\n")); wri_fonts_count = fonts_count + 1; free_ffntb(); return false; } wri_fonts[fonts_count].codepage = get_codepage(ffn, &fflen); ffn[fflen] = 0; wri_fonts[fonts_count].name = ffn; UT_DEBUGMSG((" %2d: %s (%s)\n", fonts_count, wri_fonts[fonts_count].name, wri_fonts[fonts_count].codepage)); fonts_count++; } if (fonts_count != wri_fonts_count) { wri_fonts_count = fonts_count; UT_WARNINGMSG(("read_ffntb: Wrong number of fonts.\n")); } return true; }
// // We do the actual digging into NTFS here // static BOOL _LookupStream(BOOL bFirst, struct find_stream *fs, char *szStreamMatch, struct _finddatai64_t *pfd) { char szBuf[FILENAME_MAX*2]; char *sz; HANDLE hFile; #ifdef QUERY_EXTENDED_ATTRIBUTES PFILE_EA_INFORMATION pEaInfo = (PFILE_EA_INFORMATION) abEaInfo; //PFILE_FULL_EA_INFORMATION pFullEaInfo = (PFILE_FULL_EA_INFORMATION) abEaInfo; #endif PFILE_STREAM_INFORMATION pStreamInfo = (PFILE_STREAM_INFORMATION) abStreamInfo; NTSTATUS Status; IO_STATUS_BLOCK IoStatus; struct stream_info *si, **psiPrev; if (fs->fs_next_stream_info != NULL) { goto next_si_entry; } next_fs_entry: if (!bFirst) { // if _xfindnexti64() // // Get next file in list // if (_aefindnexti64(fs->fs_handle, pfd) < 0) { if (GetLastError() == ERROR_NO_MORE_FILES) { fs->fs_bEof = TRUE; } return FALSE; // splat } } // // pfd is loaded with real file data at this point // fs->fs_fd = *pfd; // get main file info - struct copy if ((pfd->attrib & (FILE_ATTRIBUTE_DEVICE|FILE_ATTRIBUTE_REPARSE_POINT)) != 0) { return TRUE; // no streams for devices or reparse-points } if (!DynaLoad("NTDLL.DLL", "NtQueryInformationFile", &pfnNtQueryInformationFile)) { goto done; } #ifdef UNDEFINED if (!DynaLoad("NTDLL.DLL", "NtSetInformationFile", &pfnNtSetInformationFile)) { goto done; } #endif // // Build path from fs->fs_szStrippedPath minus rightmost chunk // + '\\' + pfd->name // lstrcpyn(szBuf, fs->fs_szStrippedPath, FILENAME_MAX); if ((sz = strrchr(szBuf, '\\')) == NULL) { lstrcpyn(szBuf, pfd->name, FILENAME_MAX); // should never happen } else { lstrcpyn(sz+1, pfd->name, FILENAME_MAX); } // // Open the file with Backup semantics. // // UNDOCUMENTED: CreateFile does *not* require the // SeBackupPrivilege if the mode flags are 0! // // In all other cases FILE_FLAG_BACKUP_SEMANTICS requires // the SeBackupPrivilege. // if ((hFile = CreateFile(szBuf, 0, // required - undocumented FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)) == INVALID_HANDLE_VALUE) { goto done; } #ifdef QUERY_EXTENDED_ATTRIBUTES // // First query for Extended Attributes // Status = (*pfnNtQueryInformationFile)(hFile, &IoStatus, pEaInfo, EA_BUFSIZE, FileEaInformation/*7*/); if (NT_SUCCESS(Status) && pEaInfo->EaInformationLength != 0) { // // File has Extended Attributes // fd.Attribs |= FILE_ATTRIBUTE_EA; } #endif // // Now query for streams // Status = (*pfnNtQueryInformationFile)(hFile, &IoStatus, pStreamInfo, STREAM_BUFSIZE, FileStreamInformation/*22*/); if (!NT_SUCCESS(Status)) { CloseHandle(hFile); MapNtStatusToWin32Error(Status); goto done; } if (IoStatus.Information == 0) { // pending?? CloseHandle(hFile); SetLastError(ERROR_IO_PENDING); // should never happen goto done; } CloseHandle(hFile); // // BUG: We cannot enumerate streams for "." because it looks too // much like a drive letter (".:foo"). // // It triggers bugs in FILESYSTEM_PREFIX_LEN, glob.c, basename.c, // and many other places that check for szPath[1] == ':'. // if (strcmp(pfd->name, ".") == 0) { // // Indicate that dot has streams // pfd->attrib |= FILE_ATTRIBUTE_STREAMS; // // But do not enumerate them // goto done; } psiPrev = &fs->fs_list_stream_info; *psiPrev = NULL; for (;;) { char szPath[FILENAME_MAX]; memset(szPath, 0, FILENAME_MAX); // required! // // Convert from wchar_t to multibyte string // if (!WideCharToMultiByte(get_codepage(), 0, pStreamInfo->StreamName, pStreamInfo->StreamNameLength / sizeof(WCHAR), szPath, FILENAME_MAX, NULL, NULL)) { break; // rare, ignore } if (stricmp(szPath, "::$DATA") != 0) { // skip default data stream // // Create a new stream_info obj // si = (struct stream_info *)xmalloc(sizeof(*si)); memset(si, 0, sizeof(*si)); si->si_szName = xstrdup(szPath); si->si_size = pStreamInfo->EndOfStream.QuadPart; si->si_phys_size = pStreamInfo->AllocationSize.QuadPart; // append to list *psiPrev = si; psiPrev = &si->si_next; } // // Bump to next FILE_STREAM_INFORMATION struct // if (pStreamInfo->NextEntryOffset == 0) { // end of chain *psiPrev = NULL; break; } pStreamInfo = (PFILE_STREAM_INFORMATION) (((char *)pStreamInfo) + pStreamInfo->NextEntryOffset); } // rewind to start fs->fs_next_stream_info = fs->fs_list_stream_info; if (fs->fs_list_stream_info != NULL) { // // Mark the file as having streams // pfd->attrib |= FILE_ATTRIBUTE_STREAMS; fs->fs_fd.attrib |= FILE_ATTRIBUTE_STREAMS; // mark children too } if (szStreamMatch == NULL) { // if want main file info return TRUE; // return of main file info } // fall through and return a matching stream next_si_entry: if (fs->fs_next_stream_info == NULL) { goto next_fs_entry; } // pop next si entry si = fs->fs_next_stream_info; fs->fs_next_stream_info = si->si_next; *pfd = fs->fs_fd; // get main file info - struct copy // // Turn off the directory attribute - a stream is not a directory // (although a directory can have streams) // pfd->attrib &= ~FILE_ATTRIBUTE_DIRECTORY; // // See if stream does not match pattern // // Only match on direct match or '*' // if (szStreamMatch != NULL && ((szStreamMatch[0] != ':' || szStreamMatch[1] != '*' || szStreamMatch[2] != '\0') && _mbsicmp(szStreamMatch, si->si_szName) != 0)) { // stream does not match pattern goto next_si_entry; } if (strlen(pfd->name) + strlen(si->si_szName) + 1 > sizeof(pfd->name)) { SetLastError(ERROR_BUFFER_OVERFLOW); goto done; } strcat(pfd->name, si->si_szName); // concat ":stream" if (phys_size) { pfd->size = si->si_phys_size; } else { pfd->size = si->si_size; } done: return TRUE; // return of stream info (or main file info) }
{ if (IsDBCSLeadByte(*str)) str++; len++; } return len; } #endif long double wcstold (const wchar_t * __restrict__ wcs, wchar_t ** __restrict__ wcse) { char * cs; char * cse; unsigned int i; long double ret; const unsigned int cp = get_codepage (); /* Allocate enough room for (possibly) mb chars */ cs = (char *) malloc ((wcslen(wcs)+1) * MB_CUR_MAX); if (cp == 0) /* C locale */ { for (i = 0; (wcs[i] != 0) && wcs[i] <= 255; i++) cs[i] = (char) wcs[i]; cs[i] = '\0'; } else { int nbytes = -1; int mb_len = 0; /* loop through till we hit null or invalid character */