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