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