]> git.donarmstrong.com Git - lilypond.git/blob - lily/source-file.cc
* lily/include/book.hh (class Book): idem.
[lilypond.git] / lily / source-file.cc
1 /*
2   source-file.cc -- implement Source_file
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2006 Jan Nieuwenhuizen <janneke@gnu.org>
7   Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
9
10 #include "source-file.hh"
11
12 #include "config.hh"
13
14 #if HAVE_UTF8_WCHAR_H
15 #include <utf8/wchar.h>  /* mbrtowc */
16 #else
17 #include <cwchar> /* mbrtowc */
18 #endif
19
20 #include <cstdio>
21
22 #if HAVE_SSTREAM
23 #include <sstream>
24 #else
25 #include <strstream>
26 #define istringstream(x) istrstream (x, length ())
27 #endif
28 using namespace std;
29
30 #include "file-name-map.hh"
31 #include "international.hh"
32 #include "warn.hh"
33
34 void
35 Source_file::load_stdin ()
36 {
37   characters_.clear ();
38   int c;
39   while ((c = fgetc (stdin)) != EOF)
40     characters_.push_back (c);
41
42   characters_.push_back (0);
43 }
44
45 vector<char>
46 gulp_file (string filename, int desired_size)
47 {
48   /* "b" must ensure to open literally, avoiding text (CR/LF)
49      conversions.  */
50   FILE *f = fopen (filename.c_str (), "rb");
51   if (!f)
52     {
53       warning (_f ("can't open file: `%s'", filename.c_str ()));
54
55       vector<char> cxx_arr;
56       return cxx_arr;
57     }
58
59   fseek (f, 0, SEEK_END);
60   int real_size = ftell (f);
61   int read_count = real_size;
62
63   if (desired_size > 0)
64     read_count = min (read_count, desired_size);
65   
66   rewind (f);
67
68   char *str = new char[read_count + 1];
69   str[read_count] = 0;
70
71   int bytes_read = fread (str, sizeof (char), read_count, f);
72   if (bytes_read != read_count)
73     warning (_f ("expected to read %d characters, got %d", bytes_read,
74                  read_count));
75   fclose (f);
76   int filesize = bytes_read;
77
78   vector<char> cxx_arr;
79   cxx_arr.resize (filesize);
80
81   copy (str, str + filesize, cxx_arr.begin ());
82   
83   delete[] str;
84   return cxx_arr;
85 }
86
87 void
88 Source_file::init ()
89 {
90   istream_ = 0;
91   line_offset_ = 0;
92   str_port_ = SCM_EOL;
93   self_scm_ = SCM_EOL;
94   smobify_self ();
95 }
96
97 Source_file::Source_file (string filename, string data)
98 {
99   init ();
100   
101   name_ = filename;
102
103   characters_.resize (data.length ());
104
105   copy (data.begin (), data.end (), characters_.begin ());
106
107   init_port ();
108
109   for (vsize i = 0; i < characters_.size (); i++)
110     if (characters_[i] == '\n')
111       newline_locations_.push_back (&characters_[0] + i);
112 }
113
114 Source_file::Source_file (string filename_string)
115 {
116   init ();
117   
118   name_ = filename_string;
119
120   if (filename_string == "-")
121     load_stdin ();
122   else
123     {
124       characters_ = gulp_file (filename_string, -1);
125       characters_.push_back (0);
126     }
127
128   init_port ();
129
130   for (vsize i = 0; i < characters_.size (); i++)
131     if (characters_[i] == '\n')
132       newline_locations_.push_back (&characters_[0] + i);
133 }
134
135 void
136 Source_file::init_port ()
137 {
138   SCM str = scm_makfrom0str (c_str ());
139   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
140   scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.c_str ()));
141 }
142
143
144 istream *
145 Source_file::get_istream ()
146 {
147   if (!istream_)
148     {
149       if (length ()) // can-t this be done without such a hack?
150         istream_ = new istringstream (c_str ());
151       else
152         {
153           istream_ = new istringstream ("");
154           istream_->setstate (ios::eofbit);
155           //      istream_->set (ios::eofbit);
156         }
157     }
158   return istream_;
159 }
160
161 string
162 Source_file::file_line_column_string (char const *context_str0) const
163 {
164   if (!c_str ())
165     return " (" + _ ("position unknown") + ")";
166   else
167     {
168       int l, ch, col;
169       get_counts (context_str0, &l, &ch, &col);
170
171       return name_string () + ":" + to_string (l)
172         + ":" + to_string (col);
173     }
174 }
175
176 string
177 Source_file::quote_input (char const *pos_str0) const
178 {
179   if (!contains (pos_str0))
180     return " (" + _ ("position unknown") + ")";
181
182   int l, ch, col;
183   get_counts (pos_str0, &l, &ch, &col);
184   string line = line_string (pos_str0);
185   string context = line.substr (0, ch)
186     + to_string ('\n')
187     + to_string (' ', col)
188     + line.substr (ch, line.length()-ch);
189   return context;
190 }
191
192 string
193 Source_file::name_string () const
194 {
195   return map_file_name (name_);
196 }
197
198 Source_file::~Source_file ()
199 {
200   delete istream_;
201   istream_ = 0;
202 }
203
204 Slice
205 Source_file::line_slice (char const *pos_str0) const
206 {
207   if (!contains (pos_str0))
208     return Slice (0, 0);
209
210   char const *data_str0 = c_str ();
211   char const *eof_C_ = data_str0 + length ();
212
213   if (pos_str0 == eof_C_)
214     pos_str0--;
215   char const *begin_str0 = pos_str0;
216   while (begin_str0 > data_str0)
217     if (*--begin_str0 == '\n')
218       {
219         begin_str0++;
220         break;
221       }
222
223   char const *end_str0 = pos_str0;
224   while (end_str0 < eof_C_)
225     if (*end_str0++ == '\n')
226       {
227         end_str0--;
228         break;
229       }
230
231   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
232 }
233
234 string
235 Source_file::line_string (char const *pos_str0) const
236 {
237   if (!contains (pos_str0))
238     return "";
239
240   Slice line = line_slice (pos_str0);
241   char const *data_str0 = c_str ();
242   return string (data_str0 + line[LEFT], line.length ());
243 }
244
245 void
246 Source_file::get_counts (char const *pos_str0,
247                          int *line_number,
248                          int *line_char,
249                          int *column) const
250 {
251   *line_number = 0;
252   *line_char = 0;
253   *column = 0;
254     
255   if (!contains (pos_str0))
256     return;
257
258   *line_number = get_line (pos_str0);
259
260   Slice line = line_slice (pos_str0);
261   char const *data = c_str ();
262   char const *line_start = (char const *)data + line[LEFT];
263
264   ssize left = (char const *) pos_str0 - line_start;
265   string line_begin (line_start, left);
266   char const *line_chars = line_begin.c_str ();
267
268   *column = 0;
269   *line_char = 0;
270
271   mbstate_t state;
272
273   /* Initialize the state.  */
274   memset (&state, '\0', sizeof (state));
275
276   while (left > 0)
277     {
278       wchar_t multibyte[2];
279
280       /*
281         FIXME, this is apparently locale dependent.
282       */
283 #if HAVE_MBRTOWC
284       size_t thislen = mbrtowc (multibyte, line_chars, left, &state);
285 #else
286       size_t thislen = 1;
287 #endif /* !HAVE_MBRTOWC */
288
289       /* Stop converting at invalid character;
290          this can mean we have read just the first part
291          of a valid character.  */
292       if (thislen == (size_t) -1)
293         break;
294
295       /* We want to handle embedded NUL bytes
296          but the return value is 0.  Correct this.  */
297       if (thislen == 0)
298         thislen = 1;
299
300       if (thislen == 1 && line_chars[0] == '\t')
301         (*column) = (*column / 8 + 1) * 8;
302       else
303         (*column)++;
304
305       (*line_char)++;
306       /* Advance past this character. */
307       line_chars += thislen;
308       left -= thislen;
309     }
310 }
311
312 bool
313 Source_file::contains (char const *pos_str0) const
314 {
315   return (pos_str0 && (pos_str0 >= c_str ()) && (pos_str0 <= c_str () + length ()));
316 }
317
318 int
319 Source_file::get_line (char const *pos_str0) const
320 {
321   if (!contains (pos_str0))
322     return 0;
323
324   if (!newline_locations_.size ())
325     return 1;
326
327   vsize lo = 0;
328   vsize hi = newline_locations_.size ();
329
330   if (newline_locations_[lo] > pos_str0)
331     return 1;
332
333   if (newline_locations_[hi - 1] < pos_str0)
334     return hi;
335
336   binary_search_bounds (newline_locations_,
337                         (char const*&)pos_str0,
338                         default_compare,
339                         &lo, &hi);
340
341   if (*pos_str0 == '\n')
342     lo--;
343   return lo + 2 + line_offset_;
344 }
345
346 void
347 Source_file::set_line (char const *pos_str0, int line)
348 {
349   int current_line = get_line (pos_str0);
350   line_offset_ += line - current_line;
351
352   assert (line == get_line (pos_str0));
353 }
354
355 int
356 Source_file::length () const
357 {
358   return characters_.size ();
359 }
360
361 char const *
362 Source_file::c_str () const
363 {
364   return &characters_[0];
365 }
366
367 SCM
368 Source_file::get_port () const
369 {
370   return str_port_;
371 }
372
373 /****************************************************************/
374
375 #include "ly-smobs.icc"
376
377 IMPLEMENT_SMOBS(Source_file);
378 IMPLEMENT_DEFAULT_EQUAL_P(Source_file);
379 IMPLEMENT_TYPE_P(Source_file, "ly:source-file?");
380
381 SCM
382 Source_file::mark_smob (SCM smob)
383 {
384   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
385
386   return sc->str_port_;
387 }
388
389
390 int
391 Source_file::print_smob (SCM smob, SCM port, scm_print_state *)
392 {
393   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
394
395   scm_puts ("#<Source_file ", port);
396   scm_puts (sc->name_.c_str (), port);
397
398   /* Do not print properties, that is too much hassle.  */
399   scm_puts (" >", port);
400   return 1;
401 }
402