]> git.donarmstrong.com Git - lilypond.git/blob - lily/source-file.cc
Issue 4550 (2/2) Avoid "using namespace std;" in included files
[lilypond.git] / lily / source-file.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 Jan Nieuwenhuizen <janneke@gnu.org>
5   Han-Wen Nienhuys <hanwen@xs4all.nl>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #if GCC_MAJOR < 4
22 #define _GLIBCXX_HAVE_MBSTATE_T
23 #include <wchar.h>
24 #endif /* GCC_MAJOR < 4 */
25
26 #include "source-file.hh"
27
28 #include "config.hh"
29
30 #include <cstdio>
31
32 #if HAVE_SSTREAM
33 #include <sstream>
34 #else
35 #include <strstream>
36 #endif
37
38 #include "file-name-map.hh"
39 #include "international.hh"
40 #include "misc.hh"
41 #include "warn.hh"
42
43 #if HAVE_SSTREAM
44 using std::istringstream;
45 #else
46 #define istringstream(x) std::istrstream (x, length ())
47 #endif
48
49 using std::istream;
50 using std::string;
51 using std::vector;
52
53 void
54 Source_file::load_stdin ()
55 {
56   characters_.clear ();
57   int c;
58   while ((c = fgetc (stdin)) != EOF)
59     characters_.push_back ((char)c);
60 }
61
62 /*
63   return contents of FILENAME. *Not 0-terminated!*
64  */
65 vector<char>
66 gulp_file (const string &filename, int desired_size)
67 {
68   /* "b" must ensure to open literally, avoiding text (CR/LF)
69      conversions.  */
70   FILE *f = fopen (filename.c_str (), "rb");
71   if (!f)
72     {
73       warning (_f ("cannot open file: `%s'", filename.c_str ()));
74
75       vector<char> cxx_arr;
76       return cxx_arr;
77     }
78
79   fseek (f, 0, SEEK_END);
80   int real_size = ftell (f);
81   int read_count = real_size;
82
83   if (desired_size > 0)
84     read_count = std::min (read_count, desired_size);
85
86   rewind (f);
87
88   char *str = new char[read_count + 1];
89   str[read_count] = 0;
90
91   int bytes_read = fread (str, sizeof (char), read_count, f);
92   if (bytes_read != read_count)
93     warning (_f ("expected to read %d characters, got %d", bytes_read,
94                  read_count));
95   fclose (f);
96   int filesize = bytes_read;
97
98   vector<char> cxx_arr;
99   cxx_arr.resize (filesize);
100
101   copy (str, str + filesize, cxx_arr.begin ());
102
103   delete[] str;
104   return cxx_arr;
105 }
106
107 void
108 Source_file::init ()
109 {
110   istream_ = 0;
111   line_offset_ = 0;
112   str_port_ = SCM_EOL;
113   smobify_self ();
114 }
115
116 Source_file::Source_file (const string &filename, const string &data)
117 {
118   init ();
119
120   name_ = filename;
121
122   characters_.resize (data.length ());
123   copy (data.begin (), data.end (), characters_.begin ());
124
125   characters_.push_back (0);
126
127   init_port ();
128
129   for (vsize i = 0; i < characters_.size (); i++)
130     if (characters_[i] == '\n')
131       newline_locations_.push_back (&characters_[0] + i);
132 }
133
134 Source_file::Source_file (const string &filename_string)
135 {
136   init ();
137
138   name_ = filename_string;
139
140   if (filename_string == "-")
141     load_stdin ();
142   else
143     {
144       characters_ = gulp_file (filename_string, -1);
145     }
146
147   characters_.push_back (0);
148
149   init_port ();
150
151   for (vsize i = 0; i < characters_.size (); i++)
152     if (characters_[i] == '\n')
153       newline_locations_.push_back (&characters_[0] + i);
154 }
155
156 void
157 Source_file::init_port ()
158 {
159   // This is somewhat icky: the string will in general be in utf8, but
160   // we do our own utf8 encoding and verification in the parser, so we
161   // use the no-conversion equivalent of latin1
162   SCM str = scm_from_latin1_string (c_str ());
163   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
164   scm_set_port_filename_x (str_port_, ly_string2scm (name_));
165 }
166
167 istream *
168 Source_file::get_istream ()
169 {
170   if (!istream_)
171     {
172       if (length ()) // can-t this be done without such a hack?
173         istream_ = new istringstream (c_str ());
174       else
175         {
176           istream_ = new istringstream ("");
177           istream_->setstate (std::ios::eofbit);
178           //      istream_->set (std::ios::eofbit);
179         }
180     }
181   return istream_;
182 }
183
184 string
185 Source_file::file_line_column_string (char const *context_str0) const
186 {
187   if (!c_str ())
188     return " (" + _ ("position unknown") + ")";
189   else
190     {
191       int l, ch, col, offset;
192       get_counts (context_str0, &l, &ch, &col, &offset);
193
194       return name_string () + ":" + ::to_string (l)
195              + ":" + ::to_string (col + 1);
196     }
197 }
198
199 string
200 Source_file::quote_input (char const *pos_str0) const
201 {
202   if (!contains (pos_str0))
203     return " (" + _ ("position unknown") + ")";
204
205   int l, ch, col, offset;
206   get_counts (pos_str0, &l, &ch, &col, &offset);
207   string line = line_string (pos_str0);
208   string context = line.substr (0, offset)
209                    + ::to_string ('\n')
210                    + ::to_string (' ', col)
211                    + line.substr (offset, line.length () - offset);
212   return context;
213 }
214
215 string
216 Source_file::name_string () const
217 {
218   return map_file_name (name_);
219 }
220
221 Source_file::~Source_file ()
222 {
223   delete istream_;
224 }
225
226 Slice
227 Source_file::line_slice (char const *pos_str0) const
228 {
229   if (!contains (pos_str0))
230     return Slice (0, 0);
231
232   char const *data_str0 = c_str ();
233   char const *eof_C_ = data_str0 + length ();
234
235   if (pos_str0 == eof_C_)
236     pos_str0--;
237   char const *begin_str0 = pos_str0;
238   while (begin_str0 > data_str0)
239     if (*--begin_str0 == '\n')
240       {
241         begin_str0++;
242         break;
243       }
244
245   char const *end_str0 = pos_str0;
246   while (end_str0 < eof_C_)
247     if (*end_str0++ == '\n')
248       {
249         end_str0--;
250         break;
251       }
252
253   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
254 }
255
256 string
257 Source_file::line_string (char const *pos_str0) const
258 {
259   if (!contains (pos_str0))
260     return "";
261
262   Slice line = line_slice (pos_str0);
263   char const *data_str0 = c_str ();
264   return string (data_str0 + line[LEFT], line.length ());
265 }
266
267 void
268 Source_file::get_counts (char const *pos_str0,
269                          int *line_number,
270                          int *line_char,
271                          int *column,
272                          int *byte_offset) const
273 {
274   // Initialize arguments to defaults, needed if pos_str0 is not in source
275   *line_number = 0;
276   *line_char = 0;
277   *column = 0;
278   *byte_offset = 0;
279
280   if (!contains (pos_str0))
281     return;
282
283   *line_number = get_line (pos_str0);
284
285   Slice line = line_slice (pos_str0);
286   char const *data = c_str ();
287   char const *line_start = (char const *)data + line[LEFT];
288
289   ssize left = (char const *) pos_str0 - line_start;
290   *byte_offset = left;
291
292   string line_begin (line_start, left);
293   char const *line_chars = line_begin.c_str ();
294
295   for (; left > 0; --left, ++line_chars)
296     {
297       // Skip UTF-8 continuation bytes.  This is simplistic but
298       // robust, and we warn against non-UTF-8 input in the lexer
299       // already.  In the case of non-UTF-8 or of this function being
300       // called in mid-character, the results are somewhat arbitrary,
301       // but there is no really sane definition anyway.
302       if ((*line_chars & 0xc0) == 0x80)
303         continue;
304
305       if (*line_chars == '\t')
306         (*column) = (*column / 8 + 1) * 8;
307       else
308         (*column)++;
309
310       (*line_char)++;
311     }
312 }
313
314 bool
315 Source_file::contains (char const *pos_str0) const
316 {
317   return (pos_str0 && (pos_str0 >= c_str ()) && (pos_str0 <= c_str () + length ()));
318 }
319
320 int
321 Source_file::get_line (char const *pos_str0) const
322 {
323   if (!contains (pos_str0))
324     return 0;
325
326   if (!newline_locations_.size ())
327     return 1 + line_offset_;
328
329   /* this will find the '\n' character at the end of our line */
330   vsize lo = lower_bound (newline_locations_,
331                           pos_str0,
332                           std::less<char const *> ());
333
334   /* the return value will be indexed from 1 */
335   return lo + 1 + line_offset_;
336 }
337
338 void
339 Source_file::set_line (char const *pos_str0, int line)
340 {
341   if (pos_str0)
342     {
343       int current_line = get_line (pos_str0);
344       line_offset_ += line - current_line;
345
346       assert (line == get_line (pos_str0));
347     }
348   else
349     line_offset_ = line;
350 }
351
352 int
353 Source_file::length () const
354 {
355   return characters_.size ();
356 }
357
358 char const *
359 Source_file::c_str () const
360 {
361   return &characters_[0];
362 }
363
364 SCM
365 Source_file::get_port () const
366 {
367   return str_port_;
368 }
369
370 /****************************************************************/
371
372
373 const char Source_file::type_p_name_[] = "ly:source-file?";
374
375 SCM
376 Source_file::mark_smob () const
377 {
378   return str_port_;
379 }
380
381 int
382 Source_file::print_smob (SCM port, scm_print_state *) const
383 {
384   scm_puts ("#<Source_file ", port);
385   scm_puts (name_.c_str (), port);
386
387   /* Do not print properties, that is too much hassle.  */
388   scm_puts (" >", port);
389   return 1;
390 }