1 /*
2  * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <algorithm>
27 #include <string.h>
28 #include <cerrno>
29 
30 #include "ErrorHandling.h"
31 #include "Log.h"
32 
33 
34 namespace {
35 
getFilename(const SourceCodePos & pos)36 tstring getFilename(const SourceCodePos& pos) {
37     const std::string buf(pos.file);
38     const std::string::size_type idx = buf.find_last_of("\\/");
39     if (idx == std::string::npos) {
40         return tstrings::fromUtf8(buf);
41     }
42     return tstrings::fromUtf8(buf.substr(idx + 1));
43 }
44 
reportError(const SourceCodePos & pos,const tstring & msg)45 void reportError(const SourceCodePos& pos, const tstring& msg) {
46     Logger::defaultLogger().log(Logger::LOG_ERROR, getFilename(pos).c_str(),
47         pos.lno, tstrings::fromUtf8(pos.func).c_str(), msg);
48 }
49 
50 } // namespace
51 
reportError(const SourceCodePos & pos,const std::runtime_error & e)52 void reportError(const SourceCodePos& pos, const std::runtime_error& e) {
53     reportError(pos, (tstrings::any() << "Exception with message \'"
54                                         << e.what() << "\' caught").tstr());
55 }
56 
57 
reportUnknownError(const SourceCodePos & pos)58 void reportUnknownError(const SourceCodePos& pos) {
59     reportError(pos, _T("Unknown exception caught"));
60 }
61 
62 
makeMessage(const std::runtime_error & e,const SourceCodePos & pos)63 std::string makeMessage(const std::runtime_error& e, const SourceCodePos& pos) {
64     std::ostringstream printer;
65     printer << getFilename(pos) << "(" << pos.lno << ") at "
66             << pos.func << "(): "
67             << e.what();
68     return printer.str();
69 }
70 
71 
72 namespace {
73 
isNotSpace(int chr)74 bool isNotSpace(int chr) {
75     return isspace(chr) == 0;
76 }
77 
78 
79 enum TrimMode {
80     TrimLeading = 0x10,
81     TrimTrailing = 0x20,
82     TrimBoth = TrimLeading | TrimTrailing
83 };
84 
85 // Returns position of the last printed character in the given string.
86 // Returns std::string::npos if nothing was printed.
printWithoutWhitespaces(std::ostream & out,const std::string & str,TrimMode mode)87 size_t printWithoutWhitespaces(std::ostream& out, const std::string& str,
88                                                             TrimMode mode) {
89     std::string::const_reverse_iterator it = str.rbegin();
90     std::string::const_reverse_iterator end = str.rend();
91 
92     if (mode & TrimLeading) {
93         // skip leading whitespace
94         std::string::const_iterator entry = std::find_if(str.begin(),
95                                                 str.end(), isNotSpace);
96         end = std::string::const_reverse_iterator(entry);
97     }
98 
99     if (mode & TrimTrailing) {
100         // skip trailing whitespace
101         it = std::find_if(it, end, isNotSpace);
102     }
103 
104     if (it == end) {
105         return std::string::npos;
106     }
107 
108     const size_t pos = str.rend() - end;
109     const size_t len = end - it;
110     out.write(str.c_str() + pos, len);
111     return pos + len - 1;
112 }
113 
114 } // namespace
115 
joinErrorMessages(const std::string & a,const std::string & b)116 std::string joinErrorMessages(const std::string& a, const std::string& b) {
117     const std::string endPhraseChars(";.,:!?");
118     const std::string space(" ");
119     const std::string dotAndSpace(". ");
120 
121     std::ostringstream printer;
122     printer.exceptions(std::ios::failbit | std::ios::badbit);
123 
124     size_t idx = printWithoutWhitespaces(printer, a, TrimTrailing);
125     size_t extra = 0;
126     if (idx < a.size() && endPhraseChars.find(a[idx]) == std::string::npos) {
127         printer << dotAndSpace;
128         extra = dotAndSpace.size();
129     } else if (idx != std::string::npos) {
130         printer << space;
131         extra = space.size();
132     }
133 
134     idx = printWithoutWhitespaces(printer, b, TrimBoth);
135 
136     const std::string str = printer.str();
137 
138     if (std::string::npos == idx && extra) {
139         // Nothing printed from the 'b' message. Backout delimiter string.
140         return str.substr(0, str.size() - extra);
141     }
142     return str;
143 }
144 
145 
lastCRTError()146 std::string lastCRTError() {
147 #ifndef _WIN32
148     return strerror(errno);
149 #else
150     TCHAR buffer[2048];
151     if (0 == _tcserror_s(buffer, errno)) {
152         return (tstrings::any() << buffer).str();
153     }
154     return "";
155 #endif
156 }
157