]> 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--2004 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
77 Source_file::Source_file (String filename_string)
78 {
79   name_ = filename_string;
80   istream_ = 0;
81   contents_str0_ = 0;
82
83   if (filename_string == "-")
84     load_stdin ();
85   else
86     contents_str0_ = gulp_file (filename_string, &length_);
87   
88   pos_str0_ = to_str0 ();
89
90   init_port ();
91
92   for (int i = 0; i < length_; i++)
93     if (contents_str0_[i] == '\n')
94       newline_locations_.push (contents_str0_ + i);
95 }
96
97 void
98 Source_file::init_port ()
99 {
100   SCM str = scm_makfrom0str (contents_str0_);
101   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
102   scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.get_str0 ()));
103 }
104
105 int
106 Source_file::tell () const
107 {
108   return pos_str0_  - contents_str0_; 
109 }
110
111 std::istream*
112 Source_file::get_istream ()
113 {
114   if (!istream_)
115     {
116       if (length ()) // can-t this be done without such a hack?
117         istream_ = new std::istringstream (to_str0 ());
118       else
119         {
120           istream_ = new std::istringstream ("");
121           istream_->setstate (std::ios::eofbit);
122           //      istream_->set (ios::eofbit);
123         }
124     }
125   return istream_;
126 }
127
128 String
129 Source_file::file_line_column_string (char const *context_str0) const
130 {
131   if (!to_str0 ())
132     return " (" + _ ("position unknown") + ")";
133   else
134     return name_string () + ":" + to_string (get_line (context_str0))
135       + ":" + to_string (get_char (context_str0));
136 }
137
138 String
139 Source_file::name_string () const
140 {
141   return name_;
142 }
143
144 Source_file::~Source_file ()
145 {
146   delete istream_;
147   istream_ = 0;
148   delete[] contents_str0_;
149 }
150
151 Slice
152 Source_file::line_slice (char const* pos_str0) const
153 {
154   if (!contains (pos_str0))
155     return Slice (0,0);
156
157   char const* data_str0 = to_str0 ();
158   char const * eof_C_ = data_str0 + length ();
159
160   if (pos_str0 == eof_C_)
161     pos_str0 --;
162   char const* begin_str0 = pos_str0;
163   while (begin_str0 > data_str0)
164     if (*--begin_str0 == '\n')
165       {
166         begin_str0++;
167         break;
168       }
169
170   char const* end_str0 = pos_str0;
171   while (end_str0 < eof_C_)
172     if (*end_str0++ == '\n')
173       {
174         end_str0--;
175         break;
176       }
177
178   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
179 }
180
181 String
182 Source_file::line_string (char const* pos_str0) const
183 {
184   if (!contains (pos_str0))
185     return "";
186
187   Slice line = line_slice (pos_str0);
188   char const* data_str0 = to_str0 ();
189   return String ((Byte const*)data_str0 + line[LEFT], line.length ());
190 }
191
192 int
193 Source_file::get_char (char const* pos_str0) const
194 {
195   if (!contains (pos_str0))
196     return 0;
197
198   char const* data_str0 = to_str0 ();
199   return pos_str0 - (line_slice (pos_str0)[SMALLER] + data_str0);
200 }
201
202 int
203 Source_file::get_column (char const* pos_str0) const
204 {
205   if (!contains (pos_str0))
206     return 0;
207
208   int ch_i = get_char (pos_str0);
209   String line = line_string (pos_str0);
210
211   int col_i = 0;
212   for (int i = 0; i < ch_i; i++)
213     if (line[i] == '\t')
214       col_i = (col_i / 8 + 1) * 8;
215     else
216       col_i++;
217
218   return col_i;
219 }
220
221 String
222 Source_file::error_string (char const* pos_str0) const
223 {
224   if (!contains (pos_str0))
225     return " (" + _ ("position unknown") + ")";
226
227   int ch_i = get_char (pos_str0);
228   String line = line_string (pos_str0);
229   String context = line.left_string (ch_i)
230     + to_string ('\n')
231     + to_string (' ', get_column (pos_str0))
232     + line.cut_string (ch_i, INT_MAX);
233
234   return context;
235 }
236
237 bool
238 Source_file::contains (char const* pos_str0) const
239 {
240   return (pos_str0 && (pos_str0 >= to_str0 ()) && (pos_str0 <= to_str0 () + length ()));
241 }
242
243 int
244 Source_file::get_line (char const* pos_str0) const
245 {
246   if (!contains (pos_str0))
247     return 0;
248
249   if (!newline_locations_.size ())
250     return 1;
251   
252   int lo = 0;
253   int hi = newline_locations_.size ();
254
255   if (newline_locations_[lo] > pos_str0)
256     return 1;
257   
258   if (newline_locations_[hi-1] < pos_str0)
259     return hi;
260   
261   binary_search_bounds (newline_locations_,
262                         pos_str0, 
263                         Link_array<char>::default_compare,
264                         &lo, &hi);
265
266   if (*pos_str0 == '\n')
267     lo --;
268   return lo + 2;
269 }
270
271 int
272 Source_file::length () const
273 {
274   return length_;
275 }
276
277 char const *
278 Source_file::to_str0 () const
279 {
280   return contents_str0_;
281 }
282
283 void
284 Source_file::set_pos (char const * pos_str0)
285 {
286   if (contains (pos_str0))
287     pos_str0_ = pos_str0;
288   else
289     error (error_string (pos_str0) + "invalid pos");
290 }
291
292 char const*
293 Source_file::seek_str0 (int n)
294 {
295   char const* new_str0 = to_str0 () + n;
296   if (n < 0)
297     new_str0 += length ();
298   if (contains (new_str0))
299     pos_str0_ = new_str0;
300   else
301     error (error_string (new_str0) + "seek past eof");
302
303   return pos_str0_;
304 }
305
306 char const*
307 Source_file::forward_str0 (int n)
308 {
309   char const* old_pos = pos_str0_;
310   char const* new_str0 = pos_str0_ + n;
311   if (contains (new_str0))
312     pos_str0_ = new_str0;
313   else
314     error (error_string (new_str0)  + "forward past eof");
315
316   return old_pos;
317 }
318
319 String
320 Source_file::get_string (int n)
321 {
322   String str = String ((Byte const*)forward_str0 (n), n);
323   return str;
324 }