]> git.donarmstrong.com Git - lilypond.git/blob - flower/warn.cc
Fix 1477: Add (ly:expect-warning msg args) to suppress expected warnings
[lilypond.git] / flower / warn.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "warn.hh"
21
22 #include <cstdlib>
23 #include <cstdio>
24
25 #include "std-vector.hh"
26 #include "international.hh"
27
28 using namespace std;
29
30 /** We have several different loglevels, each with its own message function(s):
31       ERROR: error, non_fatal_error, programming_error
32       WARN: warning
33       BASIC_PROGRESS: success/...
34       PROGRESS: progress_indication
35       INFO: message
36       DEBUG: debug
37   All these functions check whether the corresponding loglevel bit is set
38   and print the message only if that's the case
39 */
40
41 /* Define the loglevel (default is INFO) */
42 int loglevel = LOGLEVEL_INFO;
43
44 bool
45 is_loglevel (int level)
46 {
47   // Check the bitmask containing the loglevel
48   return (loglevel & level);
49 }
50
51 void
52 set_loglevel (int level)
53 {
54   loglevel = level;
55   debug_output (_f ("Log level set to %d\n", loglevel));
56 }
57
58 void
59 set_loglevel (string level)
60 {
61   /* Convert the loglevel string to lower-case, so we allow
62      both upper- and lower-case loglevels */
63   std::transform (level.begin (), level.end (), level.begin (), ::tolower);
64
65   /* Compare just the first few characters, so the loglevels
66      can be abbreviated */
67   if (level.compare (0, 5, "debug") == 0) // debug
68     set_loglevel (LOGLEVEL_DEBUG);
69   else if (level.compare (0, 4, "info") == 0) // info
70     set_loglevel (LOGLEVEL_INFO);
71   else if (level.compare (0, 4, "prog") == 0) // progress
72     set_loglevel (LOGLEVEL_PROGRESS);
73   else if (level.compare (0, 5, "basic") == 0) // basic progress
74     set_loglevel (LOGLEVEL_BASIC);
75   else if (level.compare (0, 4, "warn") == 0) // warning
76     set_loglevel (LOGLEVEL_WARN);
77   else if (level.compare (0, 3, "err") == 0) // error
78     set_loglevel (LOGLEVEL_ERROR);
79   else if (level.compare (0, 4, "none") == 0) // none
80     set_loglevel (LOGLEVEL_NONE);
81   else
82     {
83       int l;
84       if (sscanf (level.c_str (), "%d", &l))
85         set_loglevel (l);
86       else
87         {
88           non_fatal_error (_f ("unknown log level `%s', using default (INFO)", 
89                                level));
90           set_loglevel (LOGLEVEL_INFO);
91         }
92     }
93 }
94
95 /**
96  * Register a warning string to be expected and the output suppressed.
97  * If the warning is encountered, it will be removed from the list of
98  * expected warnings again.
99  */
100 vector<string> expected_warnings;
101 void expect_warning (string msg)
102 {
103   expected_warnings.push_back (msg);
104 }
105
106 void check_expected_warnings ()
107 {
108   if (expected_warnings.size () > 0) 
109     {
110       /* Some expected warning was not triggered, so print out a warning. */
111       string msg = _f ("%d expected warning(s) not encountered: ", 
112                        expected_warnings.size ());
113       for (vsize i = 0; i< expected_warnings.size (); i++)
114           msg += "\n        " + expected_warnings[i];
115       
116       warning (msg);
117     }
118   expected_warnings.clear ();
119 }
120
121 bool is_expected (string s)
122 {
123   bool expected = false;
124   for (vsize i = 0; i< expected_warnings.size (); i++)
125     {
126       // Compare the msg with the suppressed string; If the beginning matches,
127       // i.e. the msg can have additional content AFTER the full (exact)
128       // suppressed message, suppress the warning.
129       // This is needed for the Input class, where the message contains
130       // the input file contents after the real message.
131       if (s.compare (0, expected_warnings[i].size (), expected_warnings[i]) == 0 ) {
132         expected = true;
133         expected_warnings.erase (expected_warnings.begin () + i);
134         break;
135       }
136     }
137   return expected;
138 }
139
140
141 /**
142  * Helper functions: print_message_part (no newline prepended)
143  *                   print_message (always starts on a new line)
144  */
145
146 /* Is output message at NEWLINE?  */
147 static bool message_newline = true;
148
149 /* Display user information as a full message.
150    if newline is true, start the message on a new line.
151 */
152 void
153 print_message (int level, string location, string s, bool newline)
154 {
155   /* Only print the message if the current loglevel allows it: */
156   if (!is_loglevel (level))
157     return;
158   if (newline && !message_newline)
159     fputc ('\n', stderr);
160
161   /* Test if all silly progress_indication ("\n") can be dropped now.  */
162   if (s == "\n")
163     return;
164
165   if (!location.empty ())
166     s = location + ": " + s;
167   fputs (s.c_str (), stderr);
168   fflush (stderr);
169   if (s.length ())
170     message_newline = s[s.length () - 1] == '\n';
171 }
172
173
174 /** The actual output functions to be called in lilypond code.
175  *  Sorted in descending order of importance (errors, warnings, progress, info,
176  *  debug). Each prints a message on a separate line.
177  */
178
179 /* Display a fatal error message.  Also exits lilypond.  */
180 void
181 error (string s, string location)
182 {
183   print_message (LOG_ERROR, location, _f ("fatal error: %s", s) + "\n");
184   exit (1);
185 }
186
187 /* Display a severe programming error message, but don't exit.  */
188 void
189 programming_error (string s, string location)
190 {
191   if (is_expected (s)) {
192     print_message (LOG_DEBUG, location, _f ("suppressed programming error: %s", s) + "\n");
193   } else {
194     print_message (LOG_ERROR, location, _f ("programming error: %s", s) + "\n");
195     print_message (LOG_ERROR, location, _ ("continuing, cross fingers") + "\n");
196   }
197 }
198
199 /* Display a non-fatal error message, don't exit.  */
200 void
201 non_fatal_error (string s, string location)
202 {
203   if (is_expected (s))
204     print_message (LOG_DEBUG, location, _f ("suppressed error: %s", s) + "\n");
205   else {
206     print_message (LOG_ERROR, location, _f ("error: %s", s) + "\n");
207   }
208 }
209
210 /* Display a warning message. */
211 void
212 warning (string s, string location)
213 {
214   if (is_expected (s))
215     print_message (LOG_DEBUG, location, _f ("suppressed warning: %s", s) + "\n");
216   else {
217     // TODO: Add warning-as-error check here
218     print_message (LOG_WARN, location, _f ("warning: %s", s) + "\n");
219   }
220 }
221
222 /* Display a success message.  */
223 void
224 basic_progress (string s, string location)
225 {
226   print_message (LOG_BASIC, location, s + "\n", true);
227 }
228
229 /* Display information about the progress.  */
230 void
231 progress_indication (string s, bool newline, string location)
232 {
233   print_message (LOG_PROGRESS, location, s, newline);
234 }
235
236 /* Display a single info message.  */
237 void
238 message (string s, bool newline, string location)
239 {
240   // Use the progress loglevel for all normal messages (including progress msg)
241   print_message (LOG_INFO, location, s, newline);
242 }
243
244 /* Display a debug information, not necessarily on a new line.  */
245 void
246 debug_output (string s, bool newline, string location)
247 {
248   print_message (LOG_DEBUG, location, s, newline);
249 }