status_t write_read_attr(BNode& node, read_flags flag) { if (node.WriteAttr(B_MAIL_ATTR_READ, B_INT32_TYPE, 0, &flag, sizeof(int32)) < 0) return B_ERROR; #if R5_COMPATIBLE // manage the status string only if it currently has a "read" status BString currentStatus; if (node.ReadAttrString(B_MAIL_ATTR_STATUS, ¤tStatus) == B_OK) { if (currentStatus.ICompare("New") != 0 && currentStatus.ICompare("Read") != 0 && currentStatus.ICompare("Seen") != 0) return B_OK; } const char* statusString = (flag == B_READ) ? "Read" : (flag == B_SEEN) ? "Seen" : "New"; if (node.WriteAttr(B_MAIL_ATTR_STATUS, B_STRING_TYPE, 0, statusString, strlen(statusString)) < 0) return B_ERROR; #endif return B_OK; }
void BepdfApplication::UpdateAttr(BNode &node, const char *name, type_code type, off_t offset, void *buffer, size_t length) { char dummy[10]; if (B_ENTRY_NOT_FOUND == node.ReadAttr(name, type, offset, (char*)dummy, sizeof(dummy))) { node.WriteAttr(name, type, offset, buffer, length); } }
/* copy atribytes */ bool BF_GUI_FilesPanel_CopyTask::Copy_Atributes(BNode & o_NodeSrc,BNode & o_NodeDest) { char pcName[B_ATTR_NAME_LENGTH]; attr_info uAttrInfo; uint32 iBufMaxSize=255; char *pcBuf = (char*)malloc(iBufMaxSize); o_NodeSrc.RewindAttrs(); // while(B_OK==o_NodeSrc.GetNextAttrName(pcName)) { if(B_OK==o_NodeSrc.GetAttrInfo(pcName,&uAttrInfo)) { /* check buffer size */ if(uAttrInfo.size>iBufMaxSize) { DELETE(pcBuf); iBufMaxSize = uAttrInfo.size; pcBuf = (char*)malloc(iBufMaxSize); } /* read attr */ o_NodeSrc.ReadAttr(pcName,uAttrInfo.type,0, (void*)pcBuf,uAttrInfo.size); /* write attr */ o_NodeDest.WriteAttr(pcName,uAttrInfo.type, 0,(void*)pcBuf,uAttrInfo.size); /* check for cancel_process */ if(Canceled()) return false; } } DELETE(pcBuf); return true; }/* end of atributes */
status_t IMAPFolder::_WriteUInt32(BNode& node, const char* attribute, uint32 value) const { ssize_t bytesWritten = node.WriteAttr(attribute, B_UINT32_TYPE, 0, &value, sizeof(uint32)); if (bytesWritten == (ssize_t)sizeof(uint32)) return B_OK; return bytesWritten < 0 ? bytesWritten : B_IO_ERROR; }
status_t IMAPStorage::_WriteUniqueID(BNode& node, int32 uid) { BString uidString; uidString << uid; ssize_t written = node.WriteAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString.String(), uidString.Length()); if (written < 0) return written; return B_OK; }
/* WriteSetting * this method writes a setting to the settings file (boolean) */ void IOSettings::WriteSetting(const char* Setting, bool SettCont) { if(!SettingsFileExists()) CreateSettingsFile(); BNode objNode; objNode.SetTo(fSettingsFile.String()); if(objNode.WriteAttr(Setting, B_BOOL_TYPE, 0, (void*)&SettCont, sizeof(SettCont)) == B_FILE_ERROR) throw new IOSettingsException(new BString("can't write attr. to settings file (IOSettings::WriteSetting(const char* Setting, bool SettCont))")); }
/* WriteSetting * this method writes a setting to the settings file (strings(BString)) */ void IOSettings::WriteSetting(const char* Setting, BString SettCont) { if(!SettingsFileExists()) CreateSettingsFile(); BNode objNode; objNode.SetTo(fSettingsFile.String()); if(objNode.WriteAttr(Setting, B_STRING_TYPE, 0, SettCont.String(), SettCont.Length()) == B_FILE_ERROR) throw new IOSettingsException(new BString("can't write attr. to settings file (IOSettings::WriteSetting(const char* Setting, BString SettCont))")); }
status_t IMAPStorage::_WriteFlags(int32 flags, BNode& node) { if ((flags & kSeen) != 0) write_read_attr(node, B_READ); else write_read_attr(node, B_UNREAD); ssize_t writen = node.WriteAttr("MAIL:server_flags", B_INT32_TYPE, 0, &flags, sizeof(int32)); if (writen != sizeof(int32)) return writen; return B_OK; }
// Add or Remove OpenTracker's Restore attribute void SFileWorker::ModifyRestoreAttribute(const SFileList *fileList, bool add) { SFile *file; BNode node; for(int32 i=0; i<fileList->CountItems();i++) { file=(SFile*)fileList->ItemAt(i); if(!file) continue; if(node.SetTo(file->Ref())!=B_OK) continue; if(add) { node.WriteAttr("_trk/original_path",B_STRING_TYPE,0, file->PathDesc(),strlen(file->PathDesc())+1); } else node.RemoveAttr("_trk/original_path"); } }
/* * this method is not currently being used, but it may be useful in the * future... */ status_t DefaultCatalog::WriteToAttribute(entry_ref *appOrAddOnRef) { BNode node; status_t res = node.SetTo(appOrAddOnRef); if (res != B_OK) return res; BMallocIO mallocIO; mallocIO.SetBlockSize(max(fCatMap.Size() * 20, 256L)); // set a largish block-size in order to avoid reallocs res = Flatten(&mallocIO); if (res == B_OK) { ssize_t wsz; wsz = node.WriteAttr(BLocaleRoster::kEmbeddedCatAttr, B_MESSAGE_TYPE, 0, mallocIO.Buffer(), mallocIO.BufferLength()); if (wsz < B_OK) res = wsz; else if (wsz != (ssize_t)mallocIO.BufferLength()) res = B_ERROR; } return res; }
/* iterate over add-on-folders and collect information about each catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. */ status_t LocaleRosterData::_InitializeCatalogAddOns() { BAutolock lock(fLock); if (!lock.IsLocked()) return B_ERROR; // add info about embedded default catalog: CatalogAddOnInfo* defaultCatalogAddOnInfo = new(std::nothrow) CatalogAddOnInfo("Default", "", DefaultCatalog::kDefaultCatalogAddOnPriority); if (!defaultCatalogAddOnInfo) return B_NO_MEMORY; defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate; defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create; fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo); directory_which folders[] = { B_USER_ADDONS_DIRECTORY, B_COMMON_ADDONS_DIRECTORY, B_SYSTEM_ADDONS_DIRECTORY, }; BPath addOnPath; BDirectory addOnFolder; char buf[4096]; status_t err; for (uint32 f = 0; f < sizeof(folders) / sizeof(directory_which); ++f) { find_directory(folders[f], &addOnPath); BString addOnFolderName(addOnPath.Path()); addOnFolderName << "/locale/catalogs"; system_info info; if (get_system_info(&info) == B_OK && (info.abi & B_HAIKU_ABI_MAJOR) != (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR)) { switch (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) { case B_HAIKU_ABI_GCC_2: addOnFolderName << "/gcc2"; break; case B_HAIKU_ABI_GCC_4: addOnFolderName << "/gcc4"; break; } } err = addOnFolder.SetTo(addOnFolderName.String()); if (err != B_OK) continue; // scan through all the folder's entries for catalog add-ons: int32 count; int8 priority; entry_ref eref; BNode node; BEntry entry; dirent* dent; while ((count = addOnFolder.GetNextDirents((dirent*)buf, 4096)) > 0) { dent = (dirent*)buf; while (count-- > 0) { if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") && strcmp(dent->d_name, "gcc2") && strcmp(dent->d_name, "gcc4")) { // we have found (what should be) a catalog-add-on: eref.device = dent->d_pdev; eref.directory = dent->d_pino; eref.set_name(dent->d_name); entry.SetTo(&eref, true); // traverse through any links to get to the real thang! node.SetTo(&entry); priority = -1; if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0, &priority, sizeof(int8)) <= 0) { // add-on has no priority-attribute yet, so we load it // to fetch the priority from the corresponding // symbol... BString fullAddOnPath(addOnFolderName); fullAddOnPath << "/" << dent->d_name; image_id image = load_add_on(fullAddOnPath.String()); if (image >= B_OK) { uint8* prioPtr; if (get_image_symbol(image, "gCatalogAddOnPriority", B_SYMBOL_TYPE_DATA, (void**)&prioPtr) == B_OK) { priority = *prioPtr; node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0, &priority, sizeof(int8)); } unload_add_on(image); } } if (priority >= 0) { // add-ons with priority < 0 will be ignored CatalogAddOnInfo* addOnInfo = new(std::nothrow) CatalogAddOnInfo(dent->d_name, addOnFolderName, priority); if (addOnInfo) fCatalogAddOnInfos.AddItem((void*)addOnInfo); } } // Bump the dirent-pointer by length of the dirent just handled: dent = (dirent*)((char*)dent + dent->d_reclen); } } } fCatalogAddOnInfos.SortItems(CompareInfos); return B_OK; }
/* iterate over add-on-folders and collect information about each catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. */ status_t LocaleRosterData::_InitializeCatalogAddOns() { BAutolock lock(fLock); if (!lock.IsLocked()) return B_ERROR; // add info about embedded default catalog: CatalogAddOnInfo* defaultCatalogAddOnInfo = new(std::nothrow) CatalogAddOnInfo("Default", "", DefaultCatalog::kDefaultCatalogAddOnPriority); if (!defaultCatalogAddOnInfo) return B_NO_MEMORY; defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate; defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create; fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo); BStringList folders; BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "locale/catalogs/", B_FIND_PATH_EXISTING_ONLY, folders); BPath addOnPath; BDirectory addOnFolder; char buf[4096]; status_t err; for (int32 f = 0; f < folders.CountStrings(); f++) { BString addOnFolderName = folders.StringAt(f); err = addOnFolder.SetTo(addOnFolderName.String()); if (err != B_OK) continue; // scan through all the folder's entries for catalog add-ons: int32 count; int8 priority; entry_ref eref; BNode node; BEntry entry; dirent* dent; while ((count = addOnFolder.GetNextDirents((dirent*)buf, sizeof(buf))) > 0) { dent = (dirent*)buf; while (count-- > 0) { if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0 && strcmp(dent->d_name, "x86") != 0 && strcmp(dent->d_name, "x86_gcc2") != 0) { // we have found (what should be) a catalog-add-on: eref.device = dent->d_pdev; eref.directory = dent->d_pino; eref.set_name(dent->d_name); entry.SetTo(&eref, true); // traverse through any links to get to the real thang! node.SetTo(&entry); priority = -1; if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0, &priority, sizeof(int8)) <= 0) { // add-on has no priority-attribute yet, so we load it // to fetch the priority from the corresponding // symbol... BString fullAddOnPath(addOnFolderName); fullAddOnPath << "/" << dent->d_name; image_id image = load_add_on(fullAddOnPath.String()); if (image >= B_OK) { uint8* prioPtr; if (get_image_symbol(image, "gCatalogAddOnPriority", B_SYMBOL_TYPE_DATA, (void**)&prioPtr) == B_OK) { priority = *prioPtr; node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0, &priority, sizeof(int8)); } unload_add_on(image); } } if (priority >= 0) { // add-ons with priority < 0 will be ignored CatalogAddOnInfo* addOnInfo = new(std::nothrow) CatalogAddOnInfo(dent->d_name, addOnFolderName, priority); if (addOnInfo) fCatalogAddOnInfos.AddItem((void*)addOnInfo); } } // Bump the dirent-pointer by length of the dirent just handled: dent = (dirent*)((char*)dent + dent->d_reclen); } } } fCatalogAddOnInfos.SortItems(CompareInfos); return B_OK; }
status_t AGMSBayesianSpamFilter::ProcessMailMessage ( BPositionIO** io_message, BEntry* io_entry, BMessage* io_headers, BPath* io_folder, const char* io_uid) { ssize_t amountRead; attr_info attributeInfo; const char *classificationString; off_t dataSize; BPositionIO *dataStreamPntr = *io_message; status_t errorCode = B_OK; int32 headerLength; BString headerString; BString newSubjectString; BNode nodeForOutputFile; bool nodeForOutputFileInitialised = false; const char *oldSubjectStringPntr; char percentageString [30]; BMessage replyMessage; BMessage scriptingMessage; team_id serverTeam; float spamRatio; char *stringBuffer = NULL; char tempChar; status_t tempErrorCode; const char *tokenizeModeStringPntr; // Set up a BNode to the final output file so that we can write custom // attributes to it. Non-custom attributes are stored separately in // io_headers. if (io_entry != NULL && B_OK == nodeForOutputFile.SetTo (io_entry)) nodeForOutputFileInitialised = true; // Get a connection to the spam database server. Launch if needed, should // only need it once, unless another e-mail thread shuts down the server // inbetween messages. This code used to be in InitCheck, but apparently // that isn't called. printf("Checking for Spam Server.\n"); if (fLaunchAttemptCount == 0 || !fMessengerToServer.IsValid ()) { if (fLaunchAttemptCount > 3) goto ErrorExit; // Don't try to start the server too many times. fLaunchAttemptCount++; // Make sure the server is running. if (!be_roster->IsRunning (kServerSignature)) { errorCode = be_roster->Launch (kServerSignature); if (errorCode != B_OK) { BPath path; entry_ref ref; directory_which places[] = {B_COMMON_BIN_DIRECTORY,B_BEOS_BIN_DIRECTORY}; for (int32 i = 0; i < 2; i++) { find_directory(places[i],&path); path.Append("spamdbm"); if (!BEntry(path.Path()).Exists()) continue; get_ref_for_path(path.Path(),&ref); if ((errorCode = be_roster->Launch (&ref)) == B_OK) break; } if (errorCode != B_OK) goto ErrorExit; } } // Set up the messenger to the database server. serverTeam = be_roster->TeamFor (kServerSignature); if (serverTeam < 0) goto ErrorExit; fMessengerToServer = BMessenger (kServerSignature, serverTeam, &errorCode); if (!fMessengerToServer.IsValid ()) goto ErrorExit; // Check if the server is running in headers only mode. If so, we only // need to download the header rather than the entire message. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_GET_PROPERTY; scriptingMessage.AddSpecifier ("TokenizeMode"); replyMessage.MakeEmpty (); if ((errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage)) != B_OK) goto ErrorExit; if ((errorCode = replyMessage.FindInt32 ("error", &tempErrorCode)) != B_OK) goto ErrorExit; if ((errorCode = tempErrorCode) != B_OK) goto ErrorExit; if ((errorCode = replyMessage.FindString ("result", &tokenizeModeStringPntr)) != B_OK) goto ErrorExit; fHeaderOnly = (tokenizeModeStringPntr != NULL && strcmp (tokenizeModeStringPntr, "JustHeader") == 0); } // See if the message has already been classified. Happens for messages // which are partially downloaded when you have auto-training on. Could // untrain the partial part before training on the complete message, but we // don't know how big it was, so instead just ignore the message. if (nodeForOutputFileInitialised) { if (nodeForOutputFile.GetAttrInfo ("MAIL:classification", &attributeInfo) == B_OK) return B_OK; } // Copy the message to a string so that we can pass it to the spam database // (the even messier alternative is a temporary file). Do it in a fashion // which allows NUL bytes in the string. This method of course limits the // message size to a few hundred megabytes. If we're using header mode, // only read the header rather than the full message. if (fHeaderOnly) { // Read just the header, it ends with an empty CRLF line. dataStreamPntr->Seek (0, SEEK_SET); while ((errorCode = dataStreamPntr->Read (&tempChar, 1)) == 1) { headerString.Append (tempChar, 1); headerLength = headerString.Length(); if (headerLength >= 4 && strcmp (headerString.String() + headerLength - 4, "\r\n\r\n") == 0) break; } if (errorCode < 0) goto ErrorExit; dataSize = headerString.Length(); stringBuffer = new char [dataSize + 1]; memcpy (stringBuffer, headerString.String(), dataSize); stringBuffer[dataSize] = 0; } else { // Read the whole file. The seek to the end may take a while since // that triggers downloading of the entire message (and caching in a // slave file - see the MessageIO class). dataSize = dataStreamPntr->Seek (0, SEEK_END); if (dataSize <= 0) goto ErrorExit; try { stringBuffer = new char [dataSize + 1]; } catch (...) { errorCode = ENOMEM; goto ErrorExit; } dataStreamPntr->Seek (0, SEEK_SET); amountRead = dataStreamPntr->Read (stringBuffer, dataSize); if (amountRead != dataSize) goto ErrorExit; stringBuffer[dataSize] = 0; // Add an end of string NUL, just in case. } // Send off a scripting command to the database server, asking it to // evaluate the string for spaminess. Note that it can return ENOMSG // when there are no words (a good indicator of spam which is pure HTML // if you are using plain text only tokenization), so we could use that // as a spam marker too. Code copied for the reevaluate stuff below. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ("EvaluateString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode == ENOMSG && fNoWordsMeansSpam) spamRatio = fSpamCutoffRatio; // Yes, no words and that means spam. else if (errorCode != B_OK || replyMessage.FindFloat ("result", &spamRatio) != B_OK) goto ErrorExit; // Classification failed in one of many ways. // If we are auto-training, feed back the message to the server as a // training example (don't train if it is uncertain). Also redo the // evaluation after training. if (fAutoTraining) { if (spamRatio >= fSpamCutoffRatio || spamRatio < fGenuineCutoffRatio) { scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ((spamRatio >= fSpamCutoffRatio) ? "SpamString" : "GenuineString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode != B_OK) goto ErrorExit; // Failed to set a good example. } // Note the kind of example made so that the user doesn't reclassify // the message twice (the spam server looks for this attribute). classificationString = (spamRatio >= fSpamCutoffRatio) ? "Spam" : ((spamRatio < fGenuineCutoffRatio) ? "Genuine" : "Uncertain"); if (nodeForOutputFileInitialised) nodeForOutputFile.WriteAttr ("MAIL:classification", B_STRING_TYPE, 0 /* offset */, classificationString, strlen (classificationString) + 1); // Now that the database has changed due to training, recompute the // spam ratio. Hopefully it will have become more extreme in the // correct direction (not switched from being spam to being genuine). // Code copied from above. scriptingMessage.MakeEmpty (); scriptingMessage.what = B_SET_PROPERTY; scriptingMessage.AddSpecifier ("EvaluateString"); errorCode = scriptingMessage.AddData ("data", B_STRING_TYPE, stringBuffer, dataSize + 1, false /* fixed size */); if (errorCode != B_OK) goto ErrorExit; replyMessage.MakeEmpty (); errorCode = fMessengerToServer.SendMessage (&scriptingMessage, &replyMessage); if (errorCode != B_OK || replyMessage.FindInt32 ("error", &errorCode) != B_OK) goto ErrorExit; // Unable to read the return code. if (errorCode == ENOMSG && fNoWordsMeansSpam) spamRatio = fSpamCutoffRatio; // Yes, no words and that means spam. else if (errorCode != B_OK || replyMessage.FindFloat ("result", &spamRatio) != B_OK) goto ErrorExit; // Classification failed in one of many ways. } // Store the spam ratio in an attribute called MAIL:ratio_spam, // attached to the eventual output file. if (nodeForOutputFileInitialised) nodeForOutputFile.WriteAttr ("MAIL:ratio_spam", B_FLOAT_TYPE, 0 /* offset */, &spamRatio, sizeof (spamRatio)); // Also add it to the subject, if requested. if (fAddSpamToSubject && spamRatio >= fSpamCutoffRatio && io_headers->FindString ("Subject", &oldSubjectStringPntr) == B_OK) { newSubjectString.SetTo ("[Spam "); sprintf (percentageString, "%05.2f", spamRatio * 100.0); newSubjectString << percentageString << "%] "; newSubjectString << oldSubjectStringPntr; io_headers->ReplaceString ("Subject", newSubjectString); } // Beep using different sounds for spam and genuine, as Jeremy Friesner // nudged me to get around to implementing. And add uncertain to that, as // "BiPolar" suggested. If the user doesn't want to hear the sound, they // can turn it off in the system sound preferences. if (spamRatio >= fSpamCutoffRatio) { system_beep (kAGMSBayesBeepSpamName); } else if (spamRatio < fGenuineCutoffRatio) { system_beep (kAGMSBayesBeepGenuineName); } else { system_beep (kAGMSBayesBeepUncertainName); } return B_OK; ErrorExit: fprintf (stderr, "Error exit from " "SpamFilter::ProcessMailMessage, code maybe %ld (%s).\n", errorCode, strerror (errorCode)); delete [] stringBuffer; return B_OK; // Not MD_ERROR so the message doesn't get left on server. }
/*! Convert the plain text (UTF8) from inSource to plain or styled text in outDestination */ status_t translate_from_text(BPositionIO* source, const char* encoding, bool forceEncoding, BPositionIO* destination, uint32 outType) { if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT) return B_BAD_VALUE; // find the length of the text off_t size = source->Seek(0, SEEK_END); if (size < 0) return (status_t)size; if (size > UINT32_MAX && outType == B_STYLED_TEXT_FORMAT) return B_NOT_SUPPORTED; status_t status = source->Seek(0, SEEK_SET); if (status < B_OK) return status; if (outType == B_STYLED_TEXT_FORMAT) { // output styled text headers status = output_headers(destination, (uint32)size); if (status != B_OK) return status; } class MallocBuffer { public: MallocBuffer() : fBuffer(NULL), fSize(0) {} ~MallocBuffer() { free(fBuffer); } void* Buffer() { return fBuffer; } size_t Size() const { return fSize; } status_t Allocate(size_t size) { fBuffer = malloc(size); if (fBuffer != NULL) { fSize = size; return B_OK; } return B_NO_MEMORY; } private: void* fBuffer; size_t fSize; } encodingBuffer; BMallocIO encodingIO; uint32 encodingID = 0; // defaults to UTF-8 or no encoding BNode* node = dynamic_cast<BNode*>(source); if (node != NULL) { // determine encoding, if available const BCharacterSet* characterSet = NULL; bool hasAttribute = false; if (encoding != NULL && !forceEncoding) { BString name; if (node->ReadAttrString("be:encoding", &name) == B_OK) { encoding = name.String(); hasAttribute = true; } else { int32 value; ssize_t bytesRead = node->ReadAttr("be:encoding", B_INT32_TYPE, 0, &value, sizeof(value)); if (bytesRead == (ssize_t)sizeof(value)) { hasAttribute = true; if (value != 65535) characterSet = BCharacterSetRoster::GetCharacterSetByConversionID(value); } } } else { hasAttribute = true; // we don't write the encoding in this case } if (characterSet == NULL && encoding != NULL) characterSet = BCharacterSetRoster::FindCharacterSetByName(encoding); if (characterSet != NULL) { encodingID = characterSet->GetConversionID(); encodingBuffer.Allocate(READ_BUFFER_SIZE * 4); } if (!hasAttribute && encoding != NULL) { // add encoding attribute, so that someone opening the file can // retrieve it for persistance node->WriteAttr("be:encoding", B_STRING_TYPE, 0, encoding, strlen(encoding)); } } off_t outputSize = 0; ssize_t bytesRead; int32 state = 0; // output the actual text part of the data do { uint8 buffer[READ_BUFFER_SIZE]; bytesRead = source->Read(buffer, READ_BUFFER_SIZE); if (bytesRead < B_OK) return bytesRead; if (bytesRead == 0) break; if (encodingBuffer.Size() == 0) { // default, no encoding ssize_t bytesWritten = destination->Write(buffer, bytesRead); if (bytesWritten != bytesRead) { if (bytesWritten < B_OK) return bytesWritten; return B_ERROR; } outputSize += bytesRead; } else { // decode text file to UTF-8 char* pos = (char*)buffer; int32 encodingLength = encodingIO.BufferLength(); int32 bytesLeft = bytesRead; int32 bytes; do { encodingLength = READ_BUFFER_SIZE * 4; bytes = bytesLeft; status = convert_to_utf8(encodingID, pos, &bytes, (char*)encodingBuffer.Buffer(), &encodingLength, &state); if (status < B_OK) return status; ssize_t bytesWritten = destination->Write(encodingBuffer.Buffer(), encodingLength); if (bytesWritten < encodingLength) { if (bytesWritten < B_OK) return bytesWritten; return B_ERROR; } pos += bytes; bytesLeft -= bytes; outputSize += encodingLength; } while (encodingLength > 0 && bytesLeft > 0); } } while (bytesRead > 0); if (outType != B_STYLED_TEXT_FORMAT) return B_OK; if (encodingBuffer.Size() != 0 && size != outputSize) { if (outputSize > UINT32_MAX) return B_NOT_SUPPORTED; // we need to update the header as the decoded text size has changed status = destination->Seek(0, SEEK_SET); if (status == B_OK) status = output_headers(destination, (uint32)outputSize); if (status == B_OK) status = destination->Seek(0, SEEK_END); if (status < B_OK) return status; } // Read file attributes if outputting styled data // and source is a BNode object if (node == NULL) return B_OK; // Try to read styles - we only propagate an error if the actual on-disk // data is likely to be okay const char *kAttrName = "styles"; attr_info info; if (node->GetAttrInfo(kAttrName, &info) != B_OK) return B_OK; if (info.type != B_RAW_TYPE || info.size < 160) { // styles seem to be broken, but since we got the text, // we don't propagate the error return B_OK; } uint8* flatRunArray = new (std::nothrow) uint8[info.size]; if (flatRunArray == NULL) return B_NO_MEMORY; bytesRead = node->ReadAttr(kAttrName, B_RAW_TYPE, 0, flatRunArray, info.size); if (bytesRead != info.size) return B_OK; output_styles(destination, size, flatRunArray, info.size); delete[] flatRunArray; return B_OK; }