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