*** empty log message ***
[lilypond.git] / lily / source-file.cc
blobdee8a9e3c53c19af1e053ab8a541101492929be0
1 /*
2 source-file.cc -- implement Source_file
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Jan Nieuwenhuizen <janneke@gnu.org>
7 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 */
10 #include "source-file.hh"
12 #include "config.hh"
14 #if HAVE_UTF8_WCHAR_H
15 #include <utf8/wchar.h> /* mbrtowc */
16 #else
17 #include <wchar.h> /* mbrtowc */
18 #endif
20 #include <cstdio>
22 #if HAVE_SSTREAM
23 #include <sstream>
24 #else
25 #include <strstream>
26 #define istringstream(x) istrstream (x, length ())
27 #endif
29 #include "warn.hh"
30 #include "file-name-map.hh"
32 void
33 Source_file::load_stdin ()
35 length_ = 0;
37 int c;
38 Array<char> chs; // ugh.
39 while ((c = fgetc (stdin)) != EOF)
40 chs.push (c);
42 chs.push (0);
43 length_ = chs.size ();
44 contents_str0_ = chs.remove_array ();
47 char *
48 gulp_file (String filename, int *filesize)
50 /* "b" must ensure to open literally, avoiding text (CR/LF)
51 conversions. */
52 FILE *f = fopen (filename.to_str0 (), "rb");
53 if (!f)
55 warning (_f ("can't open file: `%s'", filename.to_str0 ()));
56 return 0;
59 fseek (f, 0, SEEK_END);
60 *filesize = ftell (f);
61 rewind (f);
63 char *str = new char[*filesize + 1];
64 str[*filesize] = 0;
66 int bytes_read = fread (str, sizeof (char), *filesize, f);
67 if (bytes_read != *filesize)
68 warning (_f ("expected to read %d characters, got %d", bytes_read,
69 *filesize));
70 fclose (f);
72 return str;
75 Source_file::Source_file (String filename, String data)
77 name_ = filename;
78 istream_ = 0;
79 contents_str0_ = data.get_copy_str0 ();
80 length_ = data.length ();
81 pos_str0_ = to_str0 ();
82 init_port ();
84 for (int i = 0; i < length_; i++)
85 if (contents_str0_[i] == '\n')
86 newline_locations_.push (contents_str0_ + i);
89 Source_file::Source_file (String filename_string)
91 name_ = filename_string;
92 istream_ = 0;
93 contents_str0_ = 0;
95 if (filename_string == "-")
96 load_stdin ();
97 else
98 contents_str0_ = gulp_file (filename_string, &length_);
100 pos_str0_ = to_str0 ();
102 init_port ();
104 for (int i = 0; i < length_; i++)
105 if (contents_str0_[i] == '\n')
106 newline_locations_.push (contents_str0_ + i);
109 void
110 Source_file::init_port ()
112 SCM str = scm_makfrom0str (contents_str0_);
113 str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
114 scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.get_str0 ()));
118 Source_file::tell () const
120 return pos_str0_ - contents_str0_;
123 std::istream*
124 Source_file::get_istream ()
126 if (!istream_)
128 if (length ()) // can-t this be done without such a hack?
129 istream_ = new std::istringstream (to_str0 ());
130 else
132 istream_ = new std::istringstream ("");
133 istream_->setstate (std::ios::eofbit);
134 // istream_->set (ios::eofbit);
137 return istream_;
140 String
141 Source_file::file_line_column_string (char const *context_str0) const
143 if (!to_str0 ())
144 return " (" + _ ("position unknown") + ")";
145 else
147 int l, ch, col;
148 get_counts (context_str0, &l, &ch, &col);
150 return name_string () + ":" + to_string (l)
151 + ":" + to_string (col);
157 String
158 Source_file::quote_input (char const* pos_str0) const
160 if (!contains (pos_str0))
161 return " (" + _ ("position unknown") + ")";
163 int l, ch, col;
164 get_counts (pos_str0, &l, &ch, &col);
165 String line = line_string (pos_str0);
166 String context = line.left_string (ch)
167 + to_string ('\n')
168 + to_string (' ', col)
169 + line.cut_string (ch, INT_MAX);
170 return context;
173 String
174 Source_file::name_string () const
176 return map_file_name (name_);
179 Source_file::~Source_file ()
181 delete istream_;
182 istream_ = 0;
183 delete[] contents_str0_;
186 Slice
187 Source_file::line_slice (char const *pos_str0) const
189 if (!contains (pos_str0))
190 return Slice (0, 0);
192 char const *data_str0 = to_str0 ();
193 char const *eof_C_ = data_str0 + length ();
195 if (pos_str0 == eof_C_)
196 pos_str0--;
197 char const *begin_str0 = pos_str0;
198 while (begin_str0 > data_str0)
199 if (*--begin_str0 == '\n')
201 begin_str0++;
202 break;
205 char const* end_str0 = pos_str0;
206 while (end_str0 < eof_C_)
207 if (*end_str0++ == '\n')
209 end_str0--;
210 break;
213 return Slice (begin_str0 - data_str0, end_str0 - data_str0);
216 String
217 Source_file::line_string (char const* pos_str0) const
219 if (!contains (pos_str0))
220 return "";
222 Slice line = line_slice (pos_str0);
223 char const *data_str0 = to_str0 ();
224 return String ((Byte const *)data_str0 + line[LEFT], line.length ());
228 void
229 Source_file::get_counts (char const *pos_str0,
230 int *line_number,
231 int *line_char,
232 int *column) const
234 if (!contains (pos_str0))
235 return;
237 *line_number = get_line (pos_str0);
239 Slice line = line_slice (pos_str0);
240 char const *data = to_str0 ();
241 Byte const *line_start = (Byte const *)data + line[LEFT];
243 int left = (Byte const*) pos_str0 - line_start;
244 String line_begin (line_start, left);
245 char const *line_chars = line_begin.to_str0();
247 *column = 0;
248 *line_char = 0;
250 mbstate_t state;
252 /* Initialize the state. */
253 memset (&state, '\0', sizeof (state));
255 while (left > 0)
257 wchar_t multibyte[2];
260 FIXME, this is apparently locale dependent.
262 size_t thislen = mbrtowc (multibyte, line_chars, left, &state);
264 /* Stop converting at invalid character;
265 this can mean we have read just the first part
266 of a valid character. */
267 if (thislen == (size_t) -1)
268 break;
270 /* We want to handle embedded NUL bytes
271 but the return value is 0. Correct this. */
272 if (thislen == 0)
273 thislen = 1;
275 if (thislen == 1 && line_chars[0] == '\t')
276 (*column) = (*column / 8 + 1) * 8;
277 else
278 (*column) ++;
280 (*line_char) ++;
281 /* Advance past this character. */
282 line_chars += thislen;
283 left -= thislen;
287 bool
288 Source_file::contains (char const* pos_str0) const
290 return (pos_str0 && (pos_str0 >= to_str0 ()) && (pos_str0 <= to_str0 () + length ()));
294 Source_file::get_line (char const* pos_str0) const
296 if (!contains (pos_str0))
297 return 0;
299 if (!newline_locations_.size ())
300 return 1;
302 int lo = 0;
303 int hi = newline_locations_.size ();
305 if (newline_locations_[lo] > pos_str0)
306 return 1;
308 if (newline_locations_[hi-1] < pos_str0)
309 return hi;
311 binary_search_bounds (newline_locations_,
312 pos_str0,
313 Link_array<char>::default_compare,
314 &lo, &hi);
316 if (*pos_str0 == '\n')
317 lo--;
318 return lo + 2;
322 Source_file::length () const
324 return length_;
327 char const *
328 Source_file::to_str0 () const
330 return contents_str0_;
333 void
334 Source_file::set_pos (char const * pos_str0)
336 if (contains (pos_str0))
337 pos_str0_ = pos_str0;
338 else
339 error (quote_input (pos_str0) + "invalid pos");
342 char const *
343 Source_file::seek_str0 (int n)
345 char const *new_str0 = to_str0 () + n;
346 if (n < 0)
347 new_str0 += length ();
348 if (contains (new_str0))
349 pos_str0_ = new_str0;
350 else
351 error (quote_input (new_str0) + "seek past eof");
353 return pos_str0_;
356 char const *
357 Source_file::forward_str0 (int n)
359 char const *old_pos = pos_str0_;
360 char const *new_str0 = pos_str0_ + n;
361 if (contains (new_str0))
362 pos_str0_ = new_str0;
363 else
364 error (quote_input (new_str0) + "forward past eof");
366 return old_pos;
369 String
370 Source_file::get_string (int n)
372 String str = String ((Byte const *)forward_str0 (n), n);
373 return str;
377 Source_file::get_port () const
379 return str_port_;