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