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