1 #pragma once
2 
3 #include <string>
4 #include <list>
5 #include <iostream>
6 #include <boost/format.hpp>
7 #include <boost/algorithm/string.hpp>
8 #include <utility>
9 #include <libintl.h>
10 #undef snprintf
11 #include <locale.h>
12 #include "AST.h"
13 #include <set>
_(const char * msgid)14 inline char * _( const char * msgid ) { return gettext( msgid ); }
_(const char * msgid,const char * msgctxt)15 inline const char * _( const char * msgid, const char *msgctxt) {
16 	/* The separator between msgctxt and msgid in a .mo file.  */
17 	const char* GETTEXT_CONTEXT_GLUE = "\004";
18 
19 	std::string str = msgctxt;
20 	str += GETTEXT_CONTEXT_GLUE;
21 	str += msgid;
22 	auto translation = dcgettext(NULL,str.c_str(), LC_MESSAGES);
23 	if(translation==str){
24 		return gettext(msgid);
25 	}else{
26 		return translation;
27 	}
28 }
29 
30 enum class message_group {
31 	Error,Warning,UI_Warning,Font_Warning,Export_Warning,Export_Error,UI_Error,Parser_Error,Trace,Deprecated,None,Echo
32 };
33 
34 
35 std::string getGroupName(const enum message_group &group);
36 std::string getGroupColor(const enum message_group &group);
37 bool getGroupTextPlain(const enum message_group &group);
38 
39 struct Message {
40 	std::string msg;
41 	Location loc;
42 	std::string docPath;
43 	enum message_group group;
44 
MessageMessage45 	Message()
46 	: msg(""), loc(Location::NONE), docPath(""), group(message_group::None)
47 	{ }
48 
MessageMessage49 	Message(const std::string& msg, const Location& loc, const std::string& docPath, const message_group& group)
50 	: msg(msg), loc(loc), docPath(docPath), group(group)
51 	{ }
52 
strMessage53 	std::string str() const {
54 		const auto g = group == message_group::None ? "" : getGroupName(group) + ": ";
55 		const auto l = loc.isNone() ? "" : " " + loc.toRelativeString(docPath);
56 		return g + msg + l;
57 	}
58 };
59 
60 typedef void (OutputHandlerFunc)(const Message &msg,void *userdata);
61 typedef void (OutputHandlerFunc2)(const Message &msg, void *userdata);
62 
63 extern OutputHandlerFunc *outputhandler;
64 extern void *outputhandler_data;
65 
66 namespace OpenSCAD {
67 	extern std::string debug;
68 	extern bool quiet;
69 	extern bool hardwarnings;
70 	extern bool parameterCheck;
71 	extern bool rangeCheck;
72 }
73 
74 void set_output_handler(OutputHandlerFunc *newhandler, OutputHandlerFunc2 *newhandler2, void *userdata);
75 void no_exceptions_for_warnings();
76 bool would_have_thrown();
77 
78 extern std::list<std::string> print_messages_stack;
79 void print_messages_push();
80 void print_messages_pop();
81 void resetSuppressedMessages();
82 
83 
84 /* PRINT statements come out in same window as ECHO.
85  usage: PRINTB("Var1: %s Var2: %i", var1 % var2 ); */
86 void PRINT(const Message &msgObj);
87 
88 void PRINT_NOCACHE(const Message &msgObj);
89 #define PRINTB_NOCACHE(_fmt, _arg) do { } while (0)
90 // #define PRINTB_NOCACHE(_fmt, _arg) do { PRINT_NOCACHE(str(boost::format(_fmt) % _arg)); } while (0)
91 
92 void PRINT_CONTEXT(const class Context *ctx, const class Module *mod, const class ModuleInstantiation *inst);
93 
94 /*PRINTD: debugging/verbose output. Usage in code:
95   CGAL_Point_3 p0(0,0,0),p1(1,0,0),p2(0,1,0);
96   PRINTD(" Created 3 points: ");
97   PRINTDB("point0, point1, point2: %s %s %s", p0 % p1 % p2 );
98   Usage on command line:
99   openscad x.scad --debug=all       # prints all debug messages
100   openscad x.scad --debug=<srcfile> # prints only debug msgs from srcfile.*.cc
101   (example: openscad --debug=export # prints only debug msgs from export.cc )
102 
103   For a debug with heavy computation cost, you can guard so that the computation
104   only occurs when debugging is turned on. For example:
105   if (OpenSCAD::debug!="") PRINTDB("PolySet dump: %s",ps->dump());
106 */
107 
108 void PRINTDEBUG(const std::string &filename,const std::string &msg);
109 #define PRINTD(_arg) do { PRINTDEBUG(std::string(__FILE__),_arg); } while (0)
110 #define PRINTDB(_fmt, _arg) do { try { PRINTDEBUG(std::string(__FILE__),str(boost::format(_fmt) % _arg)); } catch(const boost::io::format_error &e) { PRINTDEBUG(std::string(__FILE__),"bad PRINTDB usage"); } } while (0)
111 
112 std::string two_digit_exp_format( std::string doublestr );
113 std::string two_digit_exp_format( double x );
114 const std::string& quoted_string(const std::string& str);
115 
116 // extremely simple logging, eventually replace with something like boost.log
117 // usage: logstream out(5); openscad_loglevel=6; out << "hi";
118 static int openscad_loglevel = 0;
119 class logstream
120 {
121 public:
122 	std::ostream *out;
123 	int loglevel;
124 	logstream( int level = 0 ) {
125 		loglevel = level;
126 		out = &(std::cout);
127 	}
128 	template <typename T> logstream & operator<<( T const &t ) {
129 		if (out && loglevel <= openscad_loglevel) {
130 			(*out) << t ;
131 			out->flush();
132 		}
133 		return *this;
134 	}
135 };
136 
137 #define STR(s) static_cast<std::ostringstream&&>(std::ostringstream()<< s).str()
138 
139 template <typename... Ts>
140 class MessageClass
141 {
142 private:
143 	std::string fmt;
144 	std::tuple<Ts...> args;
145 	template <std::size_t... Is>
format(const std::index_sequence<Is...>)146 	std::string format(const std::index_sequence<Is...>) const
147 	{
148 
149 		std::string s;
150 		for(int i=0; fmt[i]!='\0'; i++)
151 		{
152 			if(fmt[i] == '%' && !('0' <= fmt[i+1] && fmt[i+1] <= '9'))
153 			{
154 				s.append("%%");
155 			}
156 			else
157 			{
158 				s.push_back(fmt[i]);
159 			}
160 		}
161 
162 		boost::format f(s);
163 		f.exceptions(boost::io::bad_format_string_bit);
164 		static_cast<void>(std::initializer_list<char> {(static_cast<void>(f % std::get<Is>(args)), char{}) ...});
165 		return boost::str(f);
166 	}
167 
168 public:
169 	template <typename... Args>
MessageClass(std::string && fmt,Args &&...args)170 	MessageClass(std::string&& fmt, Args&&... args) : fmt(std::forward<std::string>(fmt)), args(std::forward<Args>(args)...)
171 	{
172 	}
173 
format()174 	std::string format() const
175 	{
176 	return format(std::index_sequence_for<Ts...>{});
177 	}
178 };
179 
180 extern std::set<std::string> printedDeprecations;
181 
182 template <typename F, typename... Args>
LOG(const message_group & msg_grp,const Location & loc,const std::string & docPath,F && f,Args &&...args)183 void LOG(const message_group &msg_grp,const Location &loc,const std::string &docPath,F&& f, Args&&... args)
184 {
185 	const auto msg = MessageClass<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
186 	const auto formatted = msg.format();
187 
188 	//check for deprecations
189 	if (msg_grp == message_group::Deprecated && printedDeprecations.find(formatted+loc.toRelativeString(docPath)) != printedDeprecations.end()) return;
190 	if(msg_grp == message_group::Deprecated) printedDeprecations.insert(formatted+loc.toRelativeString(docPath));
191 
192 	Message msgObj = {formatted,loc,docPath,msg_grp};
193 
194 	PRINT(msgObj);
195 }
196