]> git.donarmstrong.com Git - lilypond.git/blob - lily/source-file.cc
* lily/smobs.cc (protect_smob): switch off fancy smob protection
[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   /* ugh, how to do neatly in STL? */
82   memcpy (&cxx_arr[0], str, filesize);
83   
84   free (str);
85   return cxx_arr;
86 }
87
88 char const * 
89 Source_file::contents_str0 () const
90 {
91   return &characters_[0];
92 }
93
94 void
95 Source_file::init ()
96 {
97   istream_ = 0;
98   line_offset_ = 0;
99   str_port_ = SCM_EOL;
100   self_scm_ = SCM_EOL;
101   smobify_self ();
102 }
103
104 Source_file::Source_file (string filename, string data)
105 {
106   init ();
107   
108   name_ = filename;
109
110   characters_.resize (data.length ());
111  
112   /* ugh, how to do neatly in STL? */
113   memcpy ((&characters_[0]), data.c_str (), data.length ());
114   
115   init_port ();
116
117   for (vsize i = 0; i < characters_.size (); i++)
118     if (characters_[i] == '\n')
119       newline_locations_.push_back (&characters_[0] + i);
120 }
121
122 Source_file::Source_file (string filename_string)
123 {
124   init ();
125   
126   name_ = filename_string;
127
128   if (filename_string == "-")
129     load_stdin ();
130   else
131     {
132       characters_ = gulp_file (filename_string, -1);
133       characters_.push_back (0);
134     }
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_makfrom0str (contents_str0 ());
147   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
148   scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.c_str ()));
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   istream_ = 0;
210 }
211
212 Slice
213 Source_file::line_slice (char const *pos_str0) const
214 {
215   if (!contains (pos_str0))
216     return Slice (0, 0);
217
218   char const *data_str0 = c_str ();
219   char const *eof_C_ = data_str0 + length ();
220
221   if (pos_str0 == eof_C_)
222     pos_str0--;
223   char const *begin_str0 = pos_str0;
224   while (begin_str0 > data_str0)
225     if (*--begin_str0 == '\n')
226       {
227         begin_str0++;
228         break;
229       }
230
231   char const *end_str0 = pos_str0;
232   while (end_str0 < eof_C_)
233     if (*end_str0++ == '\n')
234       {
235         end_str0--;
236         break;
237       }
238
239   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
240 }
241
242 string
243 Source_file::line_string (char const *pos_str0) const
244 {
245   if (!contains (pos_str0))
246     return "";
247
248   Slice line = line_slice (pos_str0);
249   char const *data_str0 = c_str ();
250   return string (data_str0 + line[LEFT], line.length ());
251 }
252
253 void
254 Source_file::get_counts (char const *pos_str0,
255                          int *line_number,
256                          int *line_char,
257                          int *column) const
258 {
259   *line_number = 0;
260   *line_char = 0;
261   *column = 0;
262     
263   if (!contains (pos_str0))
264     return;
265
266   *line_number = get_line (pos_str0);
267
268   Slice line = line_slice (pos_str0);
269   char const *data = c_str ();
270   char const *line_start = (char const *)data + line[LEFT];
271
272   ssize left = (char const *) pos_str0 - line_start;
273   string line_begin (line_start, left);
274   char const *line_chars = line_begin.c_str ();
275
276   *column = 0;
277   *line_char = 0;
278
279   mbstate_t state;
280
281   /* Initialize the state.  */
282   memset (&state, '\0', sizeof (state));
283
284   while (left > 0)
285     {
286       wchar_t multibyte[2];
287
288       /*
289         FIXME, this is apparently locale dependent.
290       */
291 #if HAVE_MBRTOWC
292       size_t thislen = mbrtowc (multibyte, line_chars, left, &state);
293 #else
294       size_t thislen = 1;
295 #endif /* !HAVE_MBRTOWC */
296
297       /* Stop converting at invalid character;
298          this can mean we have read just the first part
299          of a valid character.  */
300       if (thislen == (size_t) -1)
301         break;
302
303       /* We want to handle embedded NUL bytes
304          but the return value is 0.  Correct this.  */
305       if (thislen == 0)
306         thislen = 1;
307
308       if (thislen == 1 && line_chars[0] == '\t')
309         (*column) = (*column / 8 + 1) * 8;
310       else
311         (*column)++;
312
313       (*line_char)++;
314       /* Advance past this character. */
315       line_chars += thislen;
316       left -= thislen;
317     }
318 }
319
320 bool
321 Source_file::contains (char const *pos_str0) const
322 {
323   return (pos_str0 && (pos_str0 >= c_str ()) && (pos_str0 <= c_str () + length ()));
324 }
325
326 int
327 Source_file::get_line (char const *pos_str0) const
328 {
329   if (!contains (pos_str0))
330     return 0;
331
332   if (!newline_locations_.size ())
333     return 1;
334
335   vsize lo = 0;
336   vsize hi = newline_locations_.size ();
337
338   if (newline_locations_[lo] > pos_str0)
339     return 1;
340
341   if (newline_locations_[hi - 1] < pos_str0)
342     return hi;
343
344   binary_search_bounds (newline_locations_,
345                         (char const*&)pos_str0,
346                         default_compare,
347                         &lo, &hi);
348
349   if (*pos_str0 == '\n')
350     lo--;
351   return lo + 2 + line_offset_;
352 }
353
354 void
355 Source_file::set_line (char const *pos_str0, int line)
356 {
357   int current_line = get_line (pos_str0);
358   line_offset_ += line - current_line;
359
360   assert (line == get_line (pos_str0));
361 }
362
363 int
364 Source_file::length () const
365 {
366   return characters_.size ();
367 }
368
369 char const *
370 Source_file::c_str () const
371 {
372   return &characters_[0];
373 }
374
375 SCM
376 Source_file::get_port () const
377 {
378   return str_port_;
379 }
380
381 /****************************************************************/
382
383 #include "ly-smobs.icc"
384
385 IMPLEMENT_SMOBS(Source_file);
386 IMPLEMENT_DEFAULT_EQUAL_P(Source_file);
387 IMPLEMENT_TYPE_P(Source_file, "ly:source-file?");
388
389 SCM
390 Source_file::mark_smob (SCM smob)
391 {
392   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
393
394   return sc->str_port_;
395 }
396
397
398 int
399 Source_file::print_smob (SCM smob, SCM port, scm_print_state *)
400 {
401   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
402
403   scm_puts ("#<Source_file ", port);
404   scm_puts (sc->name_.c_str (), port);
405
406   /* Do not print properties, that is too much hassle.  */
407   scm_puts (" >", port);
408   return 1;
409 }
410