]> git.donarmstrong.com Git - lilypond.git/blob - lily/source-file.cc
*** empty log message ***
[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 #include <cstdio>
15 #if HAVE_SSTREAM
16 #include <sstream>
17 #else
18 #include <strstream>
19 #define istringstream(x) istrstream (x, length ())
20 #endif
21
22 #include "warn.hh"
23 #include "file-name-map.hh"
24
25 void
26 Source_file::load_stdin ()
27 {
28   length_ = 0;
29
30   int c;
31   Array<char> chs;              // ugh.
32   while ((c = fgetc (stdin)) != EOF)
33     chs.push (c);
34
35   chs.push (0);
36   length_ = chs.size ();
37   contents_str0_ = chs.remove_array ();
38 }
39
40 char *
41 gulp_file (String filename, int *filesize)
42 {
43   /* "b" must ensure to open literally, avoiding text (CR/LF)
44      conversions.  */
45   FILE *f = fopen (filename.to_str0 (), "rb");
46   if (!f)
47     {
48       warning (_f ("can't open file: `%s'", filename.to_str0 ()));
49       return 0;
50     }
51
52   fseek (f, 0, SEEK_END);
53   *filesize = ftell (f);
54   rewind (f);
55
56   char *str = new char[*filesize + 1];
57   str[*filesize] = 0;
58
59   int bytes_read = fread (str, sizeof (char), *filesize, f);
60   if (bytes_read != *filesize)
61     warning (_f ("expected to read %d characters, got %d", bytes_read,
62                  *filesize));
63   fclose (f);
64
65   return str;
66 }
67
68 Source_file::Source_file (String filename, String data)
69 {
70   name_ = filename;
71   istream_ = 0;
72   contents_str0_ = data.get_copy_str0 ();
73   length_ = data.length ();
74   pos_str0_ = to_str0 ();
75   init_port ();
76
77   for (int i = 0; i < length_; i++)
78     if (contents_str0_[i] == '\n')
79       newline_locations_.push (contents_str0_ + i);
80 }
81
82 Source_file::Source_file (String filename_string)
83 {
84   name_ = filename_string;
85   istream_ = 0;
86   contents_str0_ = 0;
87
88   if (filename_string == "-")
89     load_stdin ();
90   else
91     contents_str0_ = gulp_file (filename_string, &length_);
92
93   pos_str0_ = to_str0 ();
94
95   init_port ();
96
97   for (int i = 0; i < length_; i++)
98     if (contents_str0_[i] == '\n')
99       newline_locations_.push (contents_str0_ + i);
100 }
101
102 void
103 Source_file::init_port ()
104 {
105   SCM str = scm_makfrom0str (contents_str0_);
106   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
107   scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.get_str0 ()));
108 }
109
110 int
111 Source_file::tell () const
112 {
113   return pos_str0_  - contents_str0_; 
114 }
115
116 std::istream*
117 Source_file::get_istream ()
118 {
119   if (!istream_)
120     {
121       if (length ()) // can-t this be done without such a hack?
122         istream_ = new std::istringstream (to_str0 ());
123       else
124         {
125           istream_ = new std::istringstream ("");
126           istream_->setstate (std::ios::eofbit);
127           //      istream_->set (ios::eofbit);
128         }
129     }
130   return istream_;
131 }
132
133 String
134 Source_file::file_line_column_string (char const *context_str0) const
135 {
136   if (!to_str0 ())
137     return " (" + _ ("position unknown") + ")";
138   else
139     return name_string () + ":" + to_string (get_line (context_str0))
140       + ":" + to_string (get_char (context_str0));
141 }
142
143 String
144 Source_file::name_string () const
145 {
146   return map_file_name (name_);
147 }
148
149 Source_file::~Source_file ()
150 {
151   delete istream_;
152   istream_ = 0;
153   delete[] contents_str0_;
154 }
155
156 Slice
157 Source_file::line_slice (char const *pos_str0) const
158 {
159   if (!contains (pos_str0))
160     return Slice (0, 0);
161
162   char const *data_str0 = to_str0 ();
163   char const *eof_C_ = data_str0 + length ();
164
165   if (pos_str0 == eof_C_)
166     pos_str0--;
167   char const *begin_str0 = pos_str0;
168   while (begin_str0 > data_str0)
169     if (*--begin_str0 == '\n')
170       {
171         begin_str0++;
172         break;
173       }
174
175   char const* end_str0 = pos_str0;
176   while (end_str0 < eof_C_)
177     if (*end_str0++ == '\n')
178       {
179         end_str0--;
180         break;
181       }
182
183   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
184 }
185
186 String
187 Source_file::line_string (char const* pos_str0) const
188 {
189   if (!contains (pos_str0))
190     return "";
191
192   Slice line = line_slice (pos_str0);
193   char const *data_str0 = to_str0 ();
194   return String ((Byte const *)data_str0 + line[LEFT], line.length ());
195 }
196
197 int
198 Source_file::get_char (char const *pos_str0) const
199 {
200   if (!contains (pos_str0))
201     return 0;
202
203   char const *data_str0 = to_str0 ();
204   return pos_str0 - (line_slice (pos_str0)[SMALLER] + data_str0);
205 }
206
207 int
208 Source_file::get_column (char const *pos_str0) const
209 {
210   if (!contains (pos_str0))
211     return 0;
212
213   int ch_i = get_char (pos_str0);
214   String line = line_string (pos_str0);
215
216   int col_i = 0;
217   for (int i = 0; i < ch_i; i++)
218     if (line[i] == '\t')
219       col_i = (col_i / 8 + 1) * 8;
220     else
221       col_i++;
222
223   return col_i;
224 }
225
226 String
227 Source_file::error_string (char const* pos_str0) const
228 {
229   if (!contains (pos_str0))
230     return " (" + _ ("position unknown") + ")";
231
232   int ch_i = get_char (pos_str0);
233   String line = line_string (pos_str0);
234   String context = line.left_string (ch_i)
235     + to_string ('\n')
236     + to_string (' ', get_column (pos_str0))
237     + line.cut_string (ch_i, INT_MAX);
238
239   return context;
240 }
241
242 bool
243 Source_file::contains (char const* pos_str0) const
244 {
245   return (pos_str0 && (pos_str0 >= to_str0 ()) && (pos_str0 <= to_str0 () + length ()));
246 }
247
248 int
249 Source_file::get_line (char const* pos_str0) const
250 {
251   if (!contains (pos_str0))
252     return 0;
253
254   if (!newline_locations_.size ())
255     return 1;
256   
257   int lo = 0;
258   int hi = newline_locations_.size ();
259
260   if (newline_locations_[lo] > pos_str0)
261     return 1;
262   
263   if (newline_locations_[hi-1] < pos_str0)
264     return hi;
265   
266   binary_search_bounds (newline_locations_,
267                         pos_str0, 
268                         Link_array<char>::default_compare,
269                         &lo, &hi);
270
271   if (*pos_str0 == '\n')
272     lo--;
273   return lo + 2;
274 }
275
276 int
277 Source_file::length () const
278 {
279   return length_;
280 }
281
282 char const *
283 Source_file::to_str0 () const
284 {
285   return contents_str0_;
286 }
287
288 void
289 Source_file::set_pos (char const * pos_str0)
290 {
291   if (contains (pos_str0))
292     pos_str0_ = pos_str0;
293   else
294     error (error_string (pos_str0) + "invalid pos");
295 }
296
297 char const *
298 Source_file::seek_str0 (int n)
299 {
300   char const *new_str0 = to_str0 () + n;
301   if (n < 0)
302     new_str0 += length ();
303   if (contains (new_str0))
304     pos_str0_ = new_str0;
305   else
306     error (error_string (new_str0) + "seek past eof");
307
308   return pos_str0_;
309 }
310
311 char const *
312 Source_file::forward_str0 (int n)
313 {
314   char const *old_pos = pos_str0_;
315   char const *new_str0 = pos_str0_ + n;
316   if (contains (new_str0))
317     pos_str0_ = new_str0;
318   else
319     error (error_string (new_str0) + "forward past eof");
320
321   return old_pos;
322 }
323
324 String
325 Source_file::get_string (int n)
326 {
327   String str = String ((Byte const *)forward_str0 (n), n);
328   return str;
329 }