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