1 /* melder_error.cpp
2  *
3  * Copyright (C) 1992-2021 Paul Boersma
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "melder.h"
20 
defaultErrorProc(conststring32 message)21 static void defaultErrorProc (conststring32 message) {
22 	MelderConsole::write (str32str (message, U"You interrupted ") ? U"User interrupt: " : U"Error: ", true);
23 	MelderConsole::write (message, true);
24 	MelderConsole::write (U"\n", true);
25 }
defaultCrashProc(conststring32 message)26 static void defaultCrashProc (conststring32 message) {
27 	MelderConsole::write (U"Crashing bug: ", true);
28 	MelderConsole::write (message, true);
29 	MelderConsole::write (U"\n", true);
30 }
31 
32 static void (*p_theErrorProc) (conststring32 message) = & defaultErrorProc;   // initial setting after start-up; will stay if program is run from batch
33 static void (*p_theCrashProc) (conststring32 message) = & defaultCrashProc;   // initial setting after start-up; will stay if program is run from batch
34 
Melder_setErrorProc(void (* p_errorProc)(conststring32))35 void Melder_setErrorProc (void (*p_errorProc) (conststring32)) {
36 	p_theErrorProc = p_errorProc ? p_errorProc : & defaultErrorProc;
37 }
Melder_setCrashProc(void (* p_crashProc)(conststring32))38 void Melder_setCrashProc (void (*p_crashProc) (conststring32)) {
39 	p_theCrashProc = p_crashProc ? p_crashProc : & defaultCrashProc;
40 }
41 
42 constexpr integer BUFFER_LENGTH = 2000;
43 static char32 theErrorBuffer [BUFFER_LENGTH];   // safe in low-memory situations
44 
_append(conststring32 message)45 void MelderError::_append (conststring32 message) {
46 	if (! message)
47 		return;
48 	integer length = str32len (theErrorBuffer), messageLength = str32len (message);
49 	if (length + messageLength >= BUFFER_LENGTH)
50 		return;
51 	str32cpy (theErrorBuffer + length, message);
52 }
53 
Melder_hasError()54 bool Melder_hasError () {
55 	return theErrorBuffer [0] != U'\0';
56 }
57 
Melder_hasError(conststring32 partialError)58 bool Melder_hasError (conststring32 partialError) {
59 	return !! str32str (theErrorBuffer, partialError);
60 }
61 
62 #define CRASH_SEMAPHORE  U" will crash. Please notify the author "
63 
Melder_hasCrash()64 bool Melder_hasCrash () {
65 	const char32 *firstSpace = str32chr (theErrorBuffer, U' ');
66 	if (! firstSpace)
67 		return false;
68 	return Melder_stringMatchesCriterion (firstSpace, kMelder_string::STARTS_WITH, CRASH_SEMAPHORE, false);
69 }
70 
Melder_clearError()71 void Melder_clearError () {
72 	if (Melder_hasCrash ())
73 		throw MelderError ();
74 	theErrorBuffer [0] = U'\0';
75 }
76 
Melder_getError()77 conststring32 Melder_getError () {
78 	return & theErrorBuffer [0];
79 }
80 
Melder_appendError_noLine(const MelderArg & arg)81 void Melder_appendError_noLine (const MelderArg& arg) {
82 	MelderError::_append (arg._arg);
83 }
84 
Melder_flushError()85 void Melder_flushError () {
86 	if (Melder_hasCrash ()) {
87 		(*p_theCrashProc) (theErrorBuffer);
88 		abort ();
89 	} else {
90 		/*
91 			"errors" has to be cleared *before* the message is put on the screen.
92 			This is because on some platforms the message dialog is synchronous
93 			(Melder_flushError will wait until the message dialog is closed),
94 			and some operating systems may force an immediate redraw event as soon as
95 			the message dialog is closed. We want "errors" to be empty when redrawing!
96 		*/
97 		static char32 temp [BUFFER_LENGTH];
98 		str32cpy (temp, theErrorBuffer);
99 		theErrorBuffer [0] = U'\0';
100 		(*p_theErrorProc) (temp);
101 	}
102 }
103 
104 #include <mutex>
105 static std::mutex theMelder_crash_mutex;   // to guard against simultaneous crashes in multiple threads
106 
107 static const conststring32 theCrashMessage { U"Praat" CRASH_SEMAPHORE   // TODO: make dependent on app name
108 	"(paul.boersma@uva.nl) with all of the following information, "
109 	"before closing this window (and please mention in your email precisely what you were doing when this crash occurred):\n\n" };
110 
Melder_fatal_(const MelderArg & arg1,const MelderArg & arg2,const MelderArg & arg3,const MelderArg & arg4,const MelderArg & arg5,const MelderArg & arg6,const MelderArg & arg7,const MelderArg & arg8,const MelderArg & arg9,const MelderArg & arg10,const MelderArg & arg11,const MelderArg & arg12,const MelderArg & arg13)111 void Melder_fatal_ (const MelderArg& arg1,
112 	const MelderArg& arg2, const MelderArg& arg3, const MelderArg& arg4,
113 	const MelderArg& arg5, const MelderArg& arg6, const MelderArg& arg7,
114 	const MelderArg& arg8, const MelderArg& arg9, const MelderArg& arg10,
115 	const MelderArg& arg11, const MelderArg& arg12, const MelderArg& arg13)
116 {
117 	std::lock_guard <std::mutex> lock (theMelder_crash_mutex);
118 	MelderError::_append (theCrashMessage);
119 	MelderError::_append (arg1. _arg ? arg1. _arg : U"");
120 	MelderError::_append (arg2. _arg ? arg2. _arg : U"");
121 	MelderError::_append (arg3. _arg ? arg3. _arg : U"");
122 	MelderError::_append (arg4. _arg ? arg4. _arg : U"");
123 	MelderError::_append (arg5. _arg ? arg5. _arg : U"");
124 	MelderError::_append (arg6. _arg ? arg6. _arg : U"");
125 	MelderError::_append (arg7. _arg ? arg7. _arg : U"");
126 	MelderError::_append (arg8. _arg ? arg8. _arg : U"");
127 	MelderError::_append (arg9. _arg ? arg9. _arg : U"");
128 	MelderError::_append (arg10._arg ? arg10._arg : U"");
129 	MelderError::_append (arg11._arg ? arg11._arg : U"");
130 	MelderError::_append (arg12._arg ? arg12._arg : U"");
131 	MelderError::_append (arg13._arg ? arg13._arg : U"");
132 	trace (U"CRASH: ", theErrorBuffer);
133 	throw MelderError ();
134 }
135 
Melder_assert_(const char * pathName,int lineNumber,const char * condition)136 void Melder_assert_ (const char *pathName, int lineNumber, const char *condition) {
137 	/*
138 		This function tries to make sure that it allocates no heap memory.
139 		Hence, character conversion is done in place rather than with Melder_peek8to32(),
140 		and Melder_integer() is also avoided.
141 	*/
142 	std::lock_guard <std::mutex> lock (theMelder_crash_mutex);   // to guard against simultaneous crashes in multiple threads
143 	static char32 pathNameBuffer [1000], conditionBuffer [1000], lineNumberBuffer [40];
144 	Melder_8to32_inplace (pathName, pathNameBuffer, kMelder_textInputEncoding::UTF8);
145 	const char32 *p_lastFolderSeparator = str32rchr (pathNameBuffer, U'/');
146 	if (! p_lastFolderSeparator)
147 		p_lastFolderSeparator = str32rchr (pathNameBuffer, U'\\');
148 	const conststring32 fileName = ( p_lastFolderSeparator ? p_lastFolderSeparator + 1 : pathNameBuffer );
149 	Melder_8to32_inplace (condition, conditionBuffer, kMelder_textInputEncoding::UTF8);
150 	static char lineNumberBuffer8 [40];
151 	sprintf (lineNumberBuffer8, "%d", lineNumber);
152 	Melder_8to32_inplace (lineNumberBuffer8, lineNumberBuffer, kMelder_textInputEncoding::UTF8);
153 	MelderError::_append (theCrashMessage);
154 	MelderError::_append (U"Assertion failed in file \"");
155 	MelderError::_append (fileName);
156 	MelderError::_append (U"\" at line ");
157 	MelderError::_append (lineNumberBuffer);
158 	MelderError::_append (U":\n   ");
159 	MelderError::_append (conditionBuffer);
160 	MelderError::_append (U"\n\n");
161 	trace (U"CRASH: ", theErrorBuffer);
162 	throw MelderError ();
163 }
164 
165 /* End of file melder_error.cpp */
166