]> git.donarmstrong.com Git - lilypond.git/blob - lily/source-file.cc
(Source_file): use copy(). Remove contents_str0()
[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--2006 Jan Nieuwenhuizen <janneke@gnu.org>
7   Han-Wen Nienhuys <hanwen@xs4all.nl>
8 */
9
10 #include "source-file.hh"
11
12 #include "config.hh"
13
14 #if HAVE_UTF8_WCHAR_H
15 #include <utf8/wchar.h>  /* mbrtowc */
16 #else
17 #include <cwchar> /* mbrtowc */
18 #endif
19
20 #include <cstdio>
21
22 #if HAVE_SSTREAM
23 #include <sstream>
24 #else
25 #include <strstream>
26 #define istringstream(x) istrstream (x, length ())
27 #endif
28 using namespace std;
29
30 #include "file-name-map.hh"
31 #include "international.hh"
32 #include "warn.hh"
33
34 void
35 Source_file::load_stdin ()
36 {
37   characters_.clear ();
38   int c;
39   while ((c = fgetc (stdin)) != EOF)
40     characters_.push_back (c);
41
42   characters_.push_back (0);
43 }
44
45 vector<char>
46 gulp_file (string filename, int desired_size)
47 {
48   /* "b" must ensure to open literally, avoiding text (CR/LF)
49      conversions.  */
50   FILE *f = fopen (filename.c_str (), "rb");
51   if (!f)
52     {
53       warning (_f ("can't open file: `%s'", filename.c_str ()));
54
55       vector<char> cxx_arr;
56       return cxx_arr;
57     }
58
59   fseek (f, 0, SEEK_END);
60   int real_size = ftell (f);
61   int read_count = real_size;
62
63   if (desired_size > 0)
64     read_count = min (read_count, desired_size);
65   
66   rewind (f);
67
68   char *str = new char[read_count + 1];
69   str[read_count] = 0;
70
71   int bytes_read = fread (str, sizeof (char), read_count, f);
72   if (bytes_read != read_count)
73     warning (_f ("expected to read %d characters, got %d", bytes_read,
74                  read_count));
75   fclose (f);
76   int filesize = bytes_read;
77
78   vector<char> cxx_arr;
79   cxx_arr.resize (filesize);
80
81
82   copy (str, str + filesize, cxx_arr.begin ());
83   
84   free (str);
85   return cxx_arr;
86 }
87
88 void
89 Source_file::init ()
90 {
91   istream_ = 0;
92   line_offset_ = 0;
93   str_port_ = SCM_EOL;
94   self_scm_ = SCM_EOL;
95   smobify_self ();
96 }
97
98 Source_file::Source_file (string filename, string data)
99 {
100   init ();
101   
102   name_ = filename;
103
104   characters_.resize (data.length ());
105
106   copy (data.begin (), data.end (), characters_.begin ());
107
108   init_port ();
109
110   for (vsize i = 0; i < characters_.size (); i++)
111     if (characters_[i] == '\n')
112       newline_locations_.push_back (&characters_[0] + i);
113 }
114
115 Source_file::Source_file (string filename_string)
116 {
117   init ();
118   
119   name_ = filename_string;
120
121   if (filename_string == "-")
122     load_stdin ();
123   else
124     {
125       characters_ = gulp_file (filename_string, -1);
126       characters_.push_back (0);
127     }
128
129   init_port ();
130
131   for (vsize i = 0; i < characters_.size (); i++)
132     if (characters_[i] == '\n')
133       newline_locations_.push_back (&characters_[0] + i);
134 }
135
136 void
137 Source_file::init_port ()
138 {
139   SCM str = scm_makfrom0str (c_str ());
140   str_port_ = scm_mkstrport (SCM_INUM0, str, SCM_OPN | SCM_RDNG, __FUNCTION__);
141   scm_set_port_filename_x (str_port_, scm_makfrom0str (name_.c_str ()));
142 }
143
144
145 istream *
146 Source_file::get_istream ()
147 {
148   if (!istream_)
149     {
150       if (length ()) // can-t this be done without such a hack?
151         istream_ = new istringstream (c_str ());
152       else
153         {
154           istream_ = new istringstream ("");
155           istream_->setstate (ios::eofbit);
156           //      istream_->set (ios::eofbit);
157         }
158     }
159   return istream_;
160 }
161
162 string
163 Source_file::file_line_column_string (char const *context_str0) const
164 {
165   if (!c_str ())
166     return " (" + _ ("position unknown") + ")";
167   else
168     {
169       int l, ch, col;
170       get_counts (context_str0, &l, &ch, &col);
171
172       return name_string () + ":" + to_string (l)
173         + ":" + to_string (col);
174     }
175 }
176
177 string
178 Source_file::quote_input (char const *pos_str0) const
179 {
180   if (!contains (pos_str0))
181     return " (" + _ ("position unknown") + ")";
182
183   int l, ch, col;
184   get_counts (pos_str0, &l, &ch, &col);
185   string line = line_string (pos_str0);
186   string context = line.substr (0, ch)
187     + to_string ('\n')
188     + to_string (' ', col)
189     + line.substr (ch, line.length()-ch);
190   return context;
191 }
192
193 string
194 Source_file::name_string () const
195 {
196   return map_file_name (name_);
197 }
198
199 Source_file::~Source_file ()
200 {
201   delete istream_;
202   istream_ = 0;
203 }
204
205 Slice
206 Source_file::line_slice (char const *pos_str0) const
207 {
208   if (!contains (pos_str0))
209     return Slice (0, 0);
210
211   char const *data_str0 = c_str ();
212   char const *eof_C_ = data_str0 + length ();
213
214   if (pos_str0 == eof_C_)
215     pos_str0--;
216   char const *begin_str0 = pos_str0;
217   while (begin_str0 > data_str0)
218     if (*--begin_str0 == '\n')
219       {
220         begin_str0++;
221         break;
222       }
223
224   char const *end_str0 = pos_str0;
225   while (end_str0 < eof_C_)
226     if (*end_str0++ == '\n')
227       {
228         end_str0--;
229         break;
230       }
231
232   return Slice (begin_str0 - data_str0, end_str0 - data_str0);
233 }
234
235 string
236 Source_file::line_string (char const *pos_str0) const
237 {
238   if (!contains (pos_str0))
239     return "";
240
241   Slice line = line_slice (pos_str0);
242   char const *data_str0 = c_str ();
243   return string (data_str0 + line[LEFT], line.length ());
244 }
245
246 void
247 Source_file::get_counts (char const *pos_str0,
248                          int *line_number,
249                          int *line_char,
250                          int *column) const
251 {
252   *line_number = 0;
253   *line_char = 0;
254   *column = 0;
255     
256   if (!contains (pos_str0))
257     return;
258
259   *line_number = get_line (pos_str0);
260
261   Slice line = line_slice (pos_str0);
262   char const *data = c_str ();
263   char const *line_start = (char const *)data + line[LEFT];
264
265   ssize left = (char const *) pos_str0 - line_start;
266   string line_begin (line_start, left);
267   char const *line_chars = line_begin.c_str ();
268
269   *column = 0;
270   *line_char = 0;
271
272   mbstate_t state;
273
274   /* Initialize the state.  */
275   memset (&state, '\0', sizeof (state));
276
277   while (left > 0)
278     {
279       wchar_t multibyte[2];
280
281       /*
282         FIXME, this is apparently locale dependent.
283       */
284 #if HAVE_MBRTOWC
285       size_t thislen = mbrtowc (multibyte, line_chars, left, &state);
286 #else
287       size_t thislen = 1;
288 #endif /* !HAVE_MBRTOWC */
289
290       /* Stop converting at invalid character;
291          this can mean we have read just the first part
292          of a valid character.  */
293       if (thislen == (size_t) -1)
294         break;
295
296       /* We want to handle embedded NUL bytes
297          but the return value is 0.  Correct this.  */
298       if (thislen == 0)
299         thislen = 1;
300
301       if (thislen == 1 && line_chars[0] == '\t')
302         (*column) = (*column / 8 + 1) * 8;
303       else
304         (*column)++;
305
306       (*line_char)++;
307       /* Advance past this character. */
308       line_chars += thislen;
309       left -= thislen;
310     }
311 }
312
313 bool
314 Source_file::contains (char const *pos_str0) const
315 {
316   return (pos_str0 && (pos_str0 >= c_str ()) && (pos_str0 <= c_str () + length ()));
317 }
318
319 int
320 Source_file::get_line (char const *pos_str0) const
321 {
322   if (!contains (pos_str0))
323     return 0;
324
325   if (!newline_locations_.size ())
326     return 1;
327
328   vsize lo = 0;
329   vsize hi = newline_locations_.size ();
330
331   if (newline_locations_[lo] > pos_str0)
332     return 1;
333
334   if (newline_locations_[hi - 1] < pos_str0)
335     return hi;
336
337   binary_search_bounds (newline_locations_,
338                         (char const*&)pos_str0,
339                         default_compare,
340                         &lo, &hi);
341
342   if (*pos_str0 == '\n')
343     lo--;
344   return lo + 2 + line_offset_;
345 }
346
347 void
348 Source_file::set_line (char const *pos_str0, int line)
349 {
350   int current_line = get_line (pos_str0);
351   line_offset_ += line - current_line;
352
353   assert (line == get_line (pos_str0));
354 }
355
356 int
357 Source_file::length () const
358 {
359   return characters_.size ();
360 }
361
362 char const *
363 Source_file::c_str () const
364 {
365   return &characters_[0];
366 }
367
368 SCM
369 Source_file::get_port () const
370 {
371   return str_port_;
372 }
373
374 /****************************************************************/
375
376 #include "ly-smobs.icc"
377
378 IMPLEMENT_SMOBS(Source_file);
379 IMPLEMENT_DEFAULT_EQUAL_P(Source_file);
380 IMPLEMENT_TYPE_P(Source_file, "ly:source-file?");
381
382 SCM
383 Source_file::mark_smob (SCM smob)
384 {
385   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
386
387   return sc->str_port_;
388 }
389
390
391 int
392 Source_file::print_smob (SCM smob, SCM port, scm_print_state *)
393 {
394   Source_file *sc = (Source_file *) SCM_CELL_WORD_1 (smob);
395
396   scm_puts ("#<Source_file ", port);
397   scm_puts (sc->name_.c_str (), port);
398
399   /* Do not print properties, that is too much hassle.  */
400   scm_puts (" >", port);
401   return 1;
402 }
403