1 /* 2 * Copyright 2006, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "Exporter.h" 10 11 #include <fs_attr.h> 12 13 #include <Alert.h> 14 #include <File.h> 15 #include <Node.h> 16 #include <NodeInfo.h> 17 #include <Path.h> 18 #include <Roster.h> 19 #include <String.h> 20 21 #include "CommandStack.h" 22 #include "Document.h" 23 #include "Icon.h" 24 25 using std::nothrow; 26 27 // constructor 28 Exporter::Exporter() 29 : fDocument(NULL), 30 fClonedIcon(NULL), 31 fRef(), 32 fExportThread(-1), 33 fSelfDestroy(false) 34 { 35 } 36 37 // destructor 38 Exporter::~Exporter() 39 { 40 if (fExportThread >= 0 && find_thread(NULL) != fExportThread) { 41 status_t ret; 42 wait_for_thread(fExportThread, &ret); 43 } 44 45 delete fClonedIcon; 46 } 47 48 // Export 49 status_t 50 Exporter::Export(Document* document, const entry_ref& ref) 51 { 52 if (!document || ref.name == NULL) 53 return B_BAD_VALUE; 54 55 fDocument = document; 56 fClonedIcon = fDocument->Icon()->Clone(); 57 if (!fClonedIcon) 58 return B_NO_MEMORY; 59 60 fRef = ref; 61 62 fExportThread = spawn_thread(_ExportThreadEntry, "export", 63 B_NORMAL_PRIORITY, this); 64 if (fExportThread >= B_OK) 65 resume_thread(fExportThread); 66 67 return fExportThread; 68 } 69 70 // SetSelfDestroy 71 void 72 Exporter::SetSelfDestroy(bool selfDestroy) 73 { 74 fSelfDestroy = selfDestroy; 75 } 76 77 // #pragma mark - 78 79 // _ExportThreadEntry 80 int32 81 Exporter::_ExportThreadEntry(void* cookie) 82 { 83 Exporter* exporter = (Exporter*)cookie; 84 return exporter->_ExportThread(); 85 } 86 87 // _ExportThread 88 int32 89 Exporter::_ExportThread() 90 { 91 status_t ret = _Export(fClonedIcon, &fRef); 92 if (ret < B_OK) { 93 // inform user of failure at this point 94 BString helper("Saving your document failed!"); 95 helper << "\n\n" << "Error: " << strerror(ret); 96 BAlert* alert = new BAlert("bad news", helper.String(), 97 "Bleep!", NULL, NULL); 98 // launch alert asynchronously 99 alert->Go(NULL); 100 } else { 101 // success 102 103 // add to recent document list 104 be_roster->AddToRecentDocuments(&fRef); 105 // mark command stack state as saved, 106 fDocument->CommandStack()->Save(); 107 // NOTE: CommandStack is thread safe 108 109 if (fDocument->WriteLock()) { 110 // set ref and name of document 111 // fDocument->SetRef(fRef); 112 fDocument->SetName(fRef.name); 113 114 fDocument->WriteUnlock(); 115 } 116 } 117 118 if (fSelfDestroy) 119 delete this; 120 121 return ret; 122 } 123 124 // _Export 125 status_t 126 Exporter::_Export(const Icon* icon, 127 const entry_ref* docRef) 128 { 129 BEntry entry(docRef, true); 130 if (entry.IsDirectory()) 131 return B_BAD_VALUE; 132 133 const entry_ref* ref = docRef; 134 entry_ref tempRef; 135 136 if (entry.Exists()) { 137 // if the file exists create a temporary file in the same folder 138 // and hope that it doesn't already exist... 139 BPath tempPath(docRef); 140 if (tempPath.GetParent(&tempPath) >= B_OK) { 141 BString helper(docRef->name); 142 helper << system_time(); 143 if (tempPath.Append(helper.String()) >= B_OK 144 && entry.SetTo(tempPath.Path()) >= B_OK 145 && entry.GetRef(&tempRef) >= B_OK) { 146 // have the output ref point to the temporary 147 // file instead 148 ref = &tempRef; 149 } 150 } 151 } 152 153 status_t ret = B_BAD_VALUE; 154 155 // do the actual save operation into a file 156 BFile outFile(ref, B_CREATE_FILE | B_READ_WRITE | B_ERASE_FILE); 157 ret = outFile.InitCheck(); 158 if (ret == B_OK) { 159 try { 160 // export using the virtual Export() version 161 ret = Export(icon, &outFile); 162 } catch (...) { 163 printf("Exporter::_Export() - " 164 "unkown exception occured!\n"); 165 ret = B_ERROR; 166 } 167 if (ret < B_OK) { 168 printf("Exporter::_Export() - " 169 "failed to export icon: %s\n", strerror(ret)); 170 } 171 } else { 172 printf("Exporter::_Export() - " 173 "failed to create output file: %s\n", strerror(ret)); 174 } 175 outFile.Unset(); 176 177 if (ret < B_OK && ref != docRef) { 178 // in case of failure, remove temporary file 179 entry.Remove(); 180 } 181 182 if (ret >= B_OK && ref != docRef) { 183 // move temp file overwriting actual document file 184 BEntry docEntry(docRef, true); 185 // copy attributes of previous document file 186 BNode sourceNode(&docEntry); 187 BNode destNode(&entry); 188 if (sourceNode.InitCheck() >= B_OK && destNode.InitCheck() >= B_OK) { 189 // lock the nodes 190 if (sourceNode.Lock() >= B_OK) { 191 if (destNode.Lock() >= B_OK) { 192 // iterate over the attributes 193 char attrName[B_ATTR_NAME_LENGTH]; 194 while (sourceNode.GetNextAttrName(attrName) >= B_OK) { 195 attr_info info; 196 if (sourceNode.GetAttrInfo(attrName, &info) >= B_OK) { 197 char *buffer = new (nothrow) char[info.size]; 198 if (buffer && sourceNode.ReadAttr(attrName, info.type, 0, 199 buffer, info.size) == info.size) { 200 destNode.WriteAttr(attrName, info.type, 0, 201 buffer, info.size); 202 } 203 delete[] buffer; 204 } 205 } 206 destNode.Unlock(); 207 } 208 sourceNode.Unlock(); 209 } 210 } 211 // clobber the orginal file with the new temporary one 212 ret = entry.Rename(docRef->name, true); 213 } 214 215 if (ret >= B_OK && MIMEType()) { 216 // set file type 217 BNode node(docRef); 218 if (node.InitCheck() == B_OK) { 219 BNodeInfo nodeInfo(&node); 220 if (nodeInfo.InitCheck() == B_OK) 221 nodeInfo.SetType(MIMEType()); 222 } 223 } 224 return ret; 225 } 226