mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-26 07:13:49 +01:00
Strong exception safety in all uses of XMLFileWriter...
... Strong, meaning that the file at the specified path is created or modified only if all write operations complete without exceptions, barring one very unlikely possibility that a final file rename fails, but even in that case the output is successfully written to some path. This commit does not add throws, but changes the type thrown to a subclass of AudacityException, so that GuardedCall will cause the user to see an error dialog in all cases. Duplicated logic for making temporary files and backups is now all in one place, the class XMLWriter. There may be more new GuardedCalls than necessary -- the catch-all for the event loop, AudacityApp::OnExceptionInMainLoop, might be trusted instead in some cases -- but they are sufficient.
This commit is contained in:
@@ -738,243 +738,207 @@ bool AutoSaveFile::Decode(const wxString & fileName)
|
||||
return true;
|
||||
}
|
||||
|
||||
XMLFileWriter out;
|
||||
wxString tempName;
|
||||
len = file.Length() - len;
|
||||
using Chars = ArrayOf < char >;
|
||||
using WxChars = ArrayOf < wxChar >;
|
||||
Chars buf{ len };
|
||||
if (file.Read(buf.get(), len) != len)
|
||||
{
|
||||
using Chars = ArrayOf < char >;
|
||||
using WxChars = ArrayOf < wxChar >;
|
||||
Chars buf{ len };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.Read(buf.get(), len) != len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
wxMemoryInputStream in(buf.get(), len);
|
||||
|
||||
wxMemoryInputStream in(buf.get(), len);
|
||||
file.Close();
|
||||
|
||||
file.Close();
|
||||
// JKC: ANSWER-ME: Is the try catch actually doing anything?
|
||||
// If it is useful, why are we not using it everywhere?
|
||||
// If it isn't useful, why are we doing it here?
|
||||
// PRL: Yes, now we are doing GuardedCall everywhere that XMLFileWriter is
|
||||
// used.
|
||||
return GuardedCall< bool >( [&] {
|
||||
XMLFileWriter out{ fileName, _("Error Decoding File") };
|
||||
|
||||
// Decode to a temporary file to preserve the original.
|
||||
tempName = fn.CreateTempFileName(fnPath);
|
||||
bool opened = false;
|
||||
|
||||
// JKC: ANSWER-ME: Is the try catch actually doing anything?
|
||||
// If it is useful, why are we not using it everywhere?
|
||||
// If it isn't useful, why are we doing it here?
|
||||
try
|
||||
{
|
||||
out.Open(tempName, wxT("wb"));
|
||||
opened = out.IsOpened();
|
||||
}
|
||||
catch (const XMLFileWriterException&)
|
||||
{
|
||||
}
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
wxRemoveFile(tempName);
|
||||
|
||||
return false;
|
||||
}
|
||||
IdMap mIds;
|
||||
IdMapArray mIdStack;
|
||||
|
||||
mIds.clear();
|
||||
|
||||
while (!in.Eof() && !out.Error())
|
||||
while ( !in.Eof() )
|
||||
{
|
||||
short id;
|
||||
|
||||
switch (in.GetC())
|
||||
{
|
||||
case FT_Push:
|
||||
{
|
||||
mIdStack.Add(mIds);
|
||||
mIds.clear();
|
||||
}
|
||||
break;
|
||||
case FT_Push:
|
||||
{
|
||||
mIdStack.Add(mIds);
|
||||
mIds.clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Pop:
|
||||
{
|
||||
mIds = mIdStack[mIdStack.GetCount() - 1];
|
||||
mIdStack.RemoveAt(mIdStack.GetCount() - 1);
|
||||
}
|
||||
break;
|
||||
case FT_Pop:
|
||||
{
|
||||
mIds = mIdStack[mIdStack.GetCount() - 1];
|
||||
mIdStack.RemoveAt(mIdStack.GetCount() - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Name:
|
||||
{
|
||||
short len;
|
||||
case FT_Name:
|
||||
{
|
||||
short len;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars name{ len / sizeof(wxChar) };
|
||||
in.Read(name.get(), len);
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars name{ len / sizeof(wxChar) };
|
||||
in.Read(name.get(), len);
|
||||
|
||||
mIds[id] = wxString(name.get(), len / sizeof(wxChar));
|
||||
}
|
||||
break;
|
||||
mIds[id] = wxString(name.get(), len / sizeof(wxChar));
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_StartTag:
|
||||
{
|
||||
in.Read(&id, sizeof(id));
|
||||
case FT_StartTag:
|
||||
{
|
||||
in.Read(&id, sizeof(id));
|
||||
|
||||
out.StartTag(mIds[id]);
|
||||
}
|
||||
break;
|
||||
out.StartTag(mIds[id]);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_EndTag:
|
||||
{
|
||||
in.Read(&id, sizeof(id));
|
||||
case FT_EndTag:
|
||||
{
|
||||
in.Read(&id, sizeof(id));
|
||||
|
||||
out.EndTag(mIds[id]);
|
||||
}
|
||||
break;
|
||||
out.EndTag(mIds[id]);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_String:
|
||||
{
|
||||
int len;
|
||||
case FT_String:
|
||||
{
|
||||
int len;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
|
||||
out.WriteAttr(mIds[id], wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Float:
|
||||
{
|
||||
float val;
|
||||
int dig;
|
||||
case FT_Float:
|
||||
{
|
||||
float val;
|
||||
int dig;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&dig, sizeof(dig));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&dig, sizeof(dig));
|
||||
|
||||
out.WriteAttr(mIds[id], val, dig);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val, dig);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Double:
|
||||
{
|
||||
double val;
|
||||
int dig;
|
||||
case FT_Double:
|
||||
{
|
||||
double val;
|
||||
int dig;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&dig, sizeof(dig));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&dig, sizeof(dig));
|
||||
|
||||
out.WriteAttr(mIds[id], val, dig);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val, dig);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Int:
|
||||
{
|
||||
int val;
|
||||
case FT_Int:
|
||||
{
|
||||
int val;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Bool:
|
||||
{
|
||||
bool val;
|
||||
case FT_Bool:
|
||||
{
|
||||
bool val;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Long:
|
||||
{
|
||||
long val;
|
||||
case FT_Long:
|
||||
{
|
||||
long val;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_LongLong:
|
||||
{
|
||||
long long val;
|
||||
case FT_LongLong:
|
||||
{
|
||||
long long val;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_SizeT:
|
||||
{
|
||||
size_t val;
|
||||
case FT_SizeT:
|
||||
{
|
||||
size_t val;
|
||||
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
in.Read(&id, sizeof(id));
|
||||
in.Read(&val, sizeof(val));
|
||||
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
out.WriteAttr(mIds[id], val);
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Data:
|
||||
{
|
||||
int len;
|
||||
case FT_Data:
|
||||
{
|
||||
int len;
|
||||
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
|
||||
out.WriteData(wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
out.WriteData(wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
|
||||
case FT_Raw:
|
||||
{
|
||||
int len;
|
||||
case FT_Raw:
|
||||
{
|
||||
int len;
|
||||
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
in.Read(&len, sizeof(len));
|
||||
WxChars val{ len / sizeof(wxChar) };
|
||||
in.Read(val.get(), len);
|
||||
|
||||
out.Write(wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
out.Write(wxString(val.get(), len / sizeof(wxChar)));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
wxASSERT(true);
|
||||
default:
|
||||
wxASSERT(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool error = out.Error();
|
||||
|
||||
out.Close();
|
||||
out.Commit();
|
||||
|
||||
// Bail if decoding failed.
|
||||
if (error)
|
||||
{
|
||||
// File successfully decoded
|
||||
wxRemoveFile(tempName);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decoding was successful, so remove the original file and replace with decoded one.
|
||||
if (wxRemoveFile(fileName))
|
||||
{
|
||||
if (!wxRenameFile(tempName, fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user