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