OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / openvpn / buffer.c
1 /*
2  *  OpenVPN -- An application to securely tunnel IP networks
3  *             over a single UDP port, with support for SSL/TLS-based
4  *             session authentication and key exchange,
5  *             packet encryption, packet authentication, and
6  *             packet compression.
7  *
8  *  Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2
12  *  as published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program (see the file COPYING included with this
21  *  distribution); if not, write to the Free Software Foundation, Inc.,
22  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "syshead.h"
26
27 #include "common.h"
28 #include "buffer.h"
29 #include "error.h"
30 #include "mtu.h"
31 #include "thread.h"
32
33 #include "memdbg.h"
34
35 size_t
36 array_mult_safe (const size_t m1, const size_t m2, const size_t extra)
37 {
38   const size_t limit = 0xFFFFFFFF;
39   unsigned long long res = (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra;
40   if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit) || unlikely(res > (unsigned long long)limit))
41     msg (M_FATAL, "attemped allocation of excessively large array");
42   return (size_t) res;
43 }
44
45 void
46 buf_size_error (size_t size)
47 {
48   msg (M_FATAL, "fatal buffer size error, size=%lu", (unsigned long)size);
49 }
50
51 struct buffer
52 #ifdef DMALLOC
53 alloc_buf_debug (size_t size, const char *file, int line)
54 #else
55 alloc_buf (size_t size)
56 #endif
57 {
58 #ifdef DMALLOC
59   return alloc_buf_gc_debug (size, NULL, file, line);
60 #else
61   return alloc_buf_gc (size, NULL);
62 #endif
63 }
64
65 struct buffer
66 #ifdef DMALLOC
67 alloc_buf_gc_debug (size_t size, struct gc_arena *gc, const char *file, int line)
68 #else
69 alloc_buf_gc (size_t size, struct gc_arena *gc)
70 #endif
71 {
72   struct buffer buf;
73   if (!buf_size_valid (size))
74     buf_size_error (size);
75   buf.capacity = (int)size;
76   buf.offset = 0;
77   buf.len = 0;
78 #ifdef DMALLOC
79   buf.data = (uint8_t *) gc_malloc_debug (size, false, gc, file, line);
80 #else
81   buf.data = (uint8_t *) gc_malloc (size, false, gc);
82 #endif
83   if (size)
84     *buf.data = 0;
85   return buf;
86 }
87
88 struct buffer
89 #ifdef DMALLOC
90 clone_buf_debug (const struct buffer* buf, const char *file, int line)
91 #else
92 clone_buf (const struct buffer* buf)
93 #endif
94 {
95   struct buffer ret;
96   ret.capacity = buf->capacity;
97   ret.offset = buf->offset;
98   ret.len = buf->len;
99 #ifdef DMALLOC
100   ret.data = (uint8_t *) openvpn_dmalloc (file, line, buf->capacity);
101 #else
102   ret.data = (uint8_t *) malloc (buf->capacity);
103 #endif
104   check_malloc_return (ret.data);
105   memcpy (BPTR (&ret), BPTR (buf), BLEN (buf));
106   return ret;
107 }
108
109 #ifdef BUF_INIT_TRACKING
110
111 bool
112 buf_init_debug (struct buffer *buf, int offset, const char *file, int line)
113 {
114   buf->debug_file = file;
115   buf->debug_line = line;
116   return buf_init_dowork (buf, offset);
117 }
118
119 static inline int
120 buf_debug_line (const struct buffer *buf)
121 {
122   return buf->debug_line;
123 }
124
125 static const char *
126 buf_debug_file (const struct buffer *buf)
127 {
128   return buf->debug_file;
129 }
130
131 #else
132
133 #define buf_debug_line(buf) 0
134 #define buf_debug_file(buf) "[UNDEF]"
135
136 #endif
137
138 void
139 buf_clear (struct buffer *buf)
140 {
141   if (buf->capacity > 0)
142     memset (buf->data, 0, buf->capacity);
143   buf->len = 0;
144   buf->offset = 0;
145 }
146
147 bool
148 buf_assign (struct buffer *dest, const struct buffer *src)
149 {
150   if (!buf_init (dest, src->offset))
151     return false;
152   return buf_write (dest, BPTR (src), BLEN (src));
153 }
154
155 struct buffer
156 clear_buf ()
157 {
158   struct buffer buf;
159   CLEAR (buf);
160   return buf;
161 }
162
163 void
164 free_buf (struct buffer *buf)
165 {
166   if (buf->data)
167     free (buf->data);
168   CLEAR (*buf);
169 }
170
171 /*
172  * Return a buffer for write that is a subset of another buffer
173  */
174 struct buffer
175 buf_sub (struct buffer *buf, int size, bool prepend)
176 {
177   struct buffer ret;
178   uint8_t *data;
179
180   CLEAR (ret);
181   data = prepend ? buf_prepend (buf, size) : buf_write_alloc (buf, size);
182   if (data)
183     {
184       ret.capacity = size;
185       ret.data = data;
186     }
187   return ret;
188 }
189
190 /*
191  * printf append to a buffer with overflow check
192  */
193 bool
194 buf_printf (struct buffer *buf, const char *format, ...)
195 {
196   int ret = false;
197   if (buf_defined (buf))
198     {
199       va_list arglist;
200       uint8_t *ptr = BEND (buf);
201       int cap = buf_forward_capacity (buf);
202
203       if (cap > 0)
204         {
205           int stat;
206           va_start (arglist, format);
207           stat = vsnprintf ((char *)ptr, cap, format, arglist);
208           va_end (arglist);
209           *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
210           buf->len += (int) strlen ((char *)ptr);
211           if (stat >= 0 && stat < cap)
212             ret = true;
213         }
214     }
215   return ret;
216 }
217
218 /*
219  * This is necessary due to certain buggy implementations of snprintf,
220  * that don't guarantee null termination for size > 0.
221  */
222
223 int openvpn_snprintf(char *str, size_t size, const char *format, ...)
224 {
225   va_list arglist;
226   int ret = 0;
227   if (size > 0)
228     {
229       va_start (arglist, format);
230       ret = vsnprintf (str, size, format, arglist);
231       va_end (arglist);
232       str[size - 1] = 0;
233     }
234   return ret;
235 }
236
237 /*
238  * write a string to the end of a buffer that was
239  * truncated by buf_printf
240  */
241 void
242 buf_catrunc (struct buffer *buf, const char *str)
243 {
244   if (buf_forward_capacity (buf) <= 1)
245     {
246       int len = (int) strlen (str) + 1;
247       if (len < buf_forward_capacity_total (buf))
248         {
249           strncpynt ((char *)(buf->data + buf->capacity - len), str, len);
250         }
251     }
252 }
253
254 /*
255  * convert a multi-line output to one line
256  */
257 void
258 convert_to_one_line (struct buffer *buf)
259 {
260   uint8_t *cp = BPTR(buf);
261   int len = BLEN(buf);
262   while (len--)
263     {
264       if (*cp == '\n')
265         *cp = '|';
266       ++cp;
267     }
268 }
269
270 /* NOTE: requires that string be null terminated */
271 void
272 buf_write_string_file (const struct buffer *buf, const char *filename, int fd)
273 {
274   const int len = strlen ((char *) BPTR (buf));
275   const int size = write (fd, BPTR (buf), len);
276   if (size != len)
277     msg (M_ERR, "Write error on file '%s'", filename);
278 }
279
280 /*
281  * Garbage collection
282  */
283
284 void *
285 #ifdef DMALLOC
286 gc_malloc_debug (size_t size, bool clear, struct gc_arena *a, const char *file, int line)
287 #else
288 gc_malloc (size_t size, bool clear, struct gc_arena *a)
289 #endif
290 {
291   void *ret;
292   if (a)
293     {
294       struct gc_entry *e;
295 #ifdef DMALLOC
296       e = (struct gc_entry *) openvpn_dmalloc (file, line, size + sizeof (struct gc_entry));
297 #else
298       e = (struct gc_entry *) malloc (size + sizeof (struct gc_entry));
299 #endif
300       check_malloc_return (e);
301       ret = (char *) e + sizeof (struct gc_entry);
302       /*mutex_lock_static (L_GC_MALLOC);*/
303       e->next = a->list;
304       a->list = e;
305       /*mutex_unlock_static (L_GC_MALLOC);*/
306     }
307   else
308     {
309 #ifdef DMALLOC
310       ret = openvpn_dmalloc (file, line, size);
311 #else
312       ret = malloc (size);
313 #endif
314       check_malloc_return (ret);
315     }
316 #ifndef ZERO_BUFFER_ON_ALLOC
317   if (clear)
318 #endif
319     memset (ret, 0, size);
320   return ret;
321 }
322
323 void
324 x_gc_free (struct gc_arena *a)
325 {
326   struct gc_entry *e;
327   /*mutex_lock_static (L_GC_MALLOC);*/
328   e = a->list;
329   a->list = NULL;
330   /*mutex_unlock_static (L_GC_MALLOC);*/
331   
332   while (e != NULL)
333     {
334       struct gc_entry *next = e->next;
335       free (e);
336       e = next;
337     }
338 }
339
340 /*
341  * Transfer src arena to dest, resetting src to an empty arena.
342  */
343 void
344 gc_transfer (struct gc_arena *dest, struct gc_arena *src)
345 {
346   if (dest && src)
347     {
348       struct gc_entry *e = src->list;
349       if (e)
350         {
351           while (e->next != NULL)
352             e = e->next;
353           e->next = dest->list;
354           dest->list = src->list;
355           src->list = NULL;
356         }
357     }
358 }
359
360 /*
361  * Hex dump -- Output a binary buffer to a hex string and return it.
362  */
363
364 char *
365 format_hex_ex (const uint8_t *data, int size, int maxoutput,
366                int space_break, const char* separator,
367                struct gc_arena *gc)
368 {
369   struct buffer out = alloc_buf_gc (maxoutput ? maxoutput :
370                                     ((size * 2) + (size / space_break) * (int) strlen (separator) + 2),
371                                     gc);
372   int i;
373   for (i = 0; i < size; ++i)
374     {
375       if (separator && i && !(i % space_break))
376         buf_printf (&out, "%s", separator);
377       buf_printf (&out, "%02x", data[i]);
378     }
379   buf_catrunc (&out, "[more...]");
380   return (char *)out.data;
381 }
382
383 /*
384  * remove specific trailing character
385  */
386
387 void
388 buf_rmtail (struct buffer *buf, uint8_t remove)
389 {
390   uint8_t *cp = BLAST(buf);
391   if (cp && *cp == remove)
392     {
393       *cp = '\0';
394       --buf->len;
395     }
396 }
397
398 /*
399  * force a null termination even it requires
400  * truncation of the last char.
401  */
402 void
403 buf_null_terminate (struct buffer *buf)
404 {
405   char *last = (char *) BLAST (buf);
406   if (last && *last == '\0') /* already terminated? */
407     return;
408
409   if (!buf_safe (buf, 1))    /* make space for trailing null */
410     buf_inc_len (buf, -1);
411
412   buf_write_u8 (buf, 0);
413 }
414
415 /*
416  * Remove trailing \r and \n chars and ensure
417  * null termination.
418  */
419 void
420 buf_chomp (struct buffer *buf)
421 {
422   while (true)
423     {
424       char *last = (char *) BLAST (buf);
425       if (!last)
426         break;
427       if (char_class (*last, CC_CRLF|CC_NULL))
428         {
429           if (!buf_inc_len (buf, -1))
430             break;
431         }
432       else
433         break;
434     }
435   buf_null_terminate (buf);
436 }
437
438 const char *
439 skip_leading_whitespace (const char *str)
440 {
441   while (*str)
442     {
443       const char c = *str;
444       if (!(c == ' ' || c == '\t'))
445         break;
446       ++str;
447     }
448   return str;
449 }
450
451 /*
452  * like buf_null_terminate, but operate on strings
453  */
454 void
455 string_null_terminate (char *str, int len, int capacity)
456 {
457   ASSERT (len >= 0 && len <= capacity && capacity > 0);
458   if (len < capacity)
459     *(str + len) = '\0';
460   else if (len == capacity)
461     *(str + len - 1) = '\0';
462 }
463
464 /*
465  * Remove trailing \r and \n chars.
466  */
467 void
468 chomp (char *str)
469 {
470   rm_trailing_chars (str, "\r\n");
471 }
472
473 /*
474  * Remove trailing chars
475  */
476 void
477 rm_trailing_chars (char *str, const char *what_to_delete)
478 {
479   bool modified;
480   do {
481     const int len = strlen (str);
482     modified = false;
483     if (len > 0)
484       {
485         char *cp = str + (len - 1);
486         if (strchr (what_to_delete, *cp) != NULL)
487           {
488             *cp = '\0';
489             modified = true;
490           }
491       }
492   } while (modified);
493 }
494
495 /*
496  * Allocate a string
497  */
498 char *
499 #ifdef DMALLOC
500 string_alloc_debug (const char *str, struct gc_arena *gc, const char *file, int line)
501 #else
502 string_alloc (const char *str, struct gc_arena *gc)
503 #endif
504 {
505   if (str)
506     {
507       const int n = strlen (str) + 1;
508       char *ret;
509
510 #ifdef DMALLOC
511       ret = (char *) gc_malloc_debug (n, false, gc, file, line);
512 #else
513       ret = (char *) gc_malloc (n, false, gc);
514 #endif
515       memcpy (ret, str, n);
516       return ret;
517     }
518   else
519     return NULL;
520 }
521
522 /*
523  * Erase all characters in a string
524  */
525 void
526 string_clear (char *str)
527 {
528   if (str)
529     {
530       const int len = strlen (str);
531       if (len > 0)
532         memset (str, 0, len);
533     }
534 }
535
536 /*
537  * Return the length of a string array
538  */
539 int
540 string_array_len (const char **array)
541 {
542   int i = 0;
543   if (array)
544     {
545       while (array[i])
546         ++i;
547     }
548   return i;
549 }
550
551 char *
552 print_argv (const char **p, struct gc_arena *gc, const unsigned int flags)
553 {
554   struct buffer out = alloc_buf_gc (256, gc);
555   int i = 0;
556   for (;;)
557     {
558       const char *cp = *p++;
559       if (!cp)
560         break;
561       if (i)
562         buf_printf (&out, " ");
563       if (flags & PA_BRACKET)
564         buf_printf (&out, "[%s]", cp);
565       else
566         buf_printf (&out, "%s", cp);
567       ++i;
568     }
569   return BSTR (&out);
570 }
571
572 /*
573  * Allocate a string inside a buffer
574  */
575 struct buffer
576 #ifdef DMALLOC
577 string_alloc_buf_debug (const char *str, struct gc_arena *gc, const char *file, int line)
578 #else
579 string_alloc_buf (const char *str, struct gc_arena *gc)
580 #endif
581 {
582   struct buffer buf;
583
584   ASSERT (str);
585
586 #ifdef DMALLOC
587   buf_set_read (&buf, (uint8_t*) string_alloc_debug (str, gc, file, line), strlen (str) + 1);
588 #else
589   buf_set_read (&buf, (uint8_t*) string_alloc (str, gc), strlen (str) + 1);
590 #endif
591
592   if (buf.len > 0) /* Don't count trailing '\0' as part of length */
593     --buf.len;
594
595   return buf;
596 }
597
598 /*
599  * String comparison
600  */
601
602 bool
603 buf_string_match_head_str (const struct buffer *src, const char *match)
604 {
605   const int size = strlen (match);
606   if (size < 0 || size > src->len)
607     return false;
608   return memcmp (BPTR (src), match, size) == 0;
609 }
610
611 bool
612 buf_string_compare_advance (struct buffer *src, const char *match)
613 {
614   if (buf_string_match_head_str (src, match))
615     {
616       buf_advance (src, strlen (match));
617       return true;
618     }
619   else
620     return false;
621 }
622
623 int
624 buf_substring_len (const struct buffer *buf, int delim)
625 {
626   int i = 0;
627   struct buffer tmp = *buf;
628   int c;
629
630   while ((c = buf_read_u8 (&tmp)) >= 0)
631     {
632       ++i;
633       if (c == delim)
634         return i;
635     }
636   return -1;
637 }
638
639 /*
640  * String parsing
641  */
642
643 bool
644 buf_parse (struct buffer *buf, const int delim, char *line, const int size)
645 {
646   bool eol = false;
647   int n = 0;
648   int c;
649
650   ASSERT (size > 0);
651
652   do
653     {
654       c = buf_read_u8 (buf);
655       if (c < 0)
656         eol = true;
657       if (c <= 0 || c == delim)
658         c = 0;
659       if (n >= size)
660         break;
661       line[n++] = c;
662     }
663   while (c);
664
665   line[size-1] = '\0';
666   return !(eol && !strlen (line));
667 }
668
669 /*
670  * Print a string which might be NULL
671  */
672 const char *
673 np (const char *str)
674 {
675   if (str)
676     return str;
677   else
678     return "[NULL]";
679 }
680
681 /*
682  * Classify and mutate strings based on character types.
683  */
684
685 bool
686 char_class (const unsigned char c, const unsigned int flags)
687 {
688   if (!flags)
689     return false;
690   if (flags & CC_ANY)
691     return true;
692
693   if ((flags & CC_NULL) && c == '\0')
694     return true;
695
696   if ((flags & CC_ALNUM) && isalnum (c))
697     return true;
698   if ((flags & CC_ALPHA) && isalpha (c))
699     return true;
700   if ((flags & CC_ASCII) && isascii (c))
701     return true;
702   if ((flags & CC_CNTRL) && iscntrl (c))
703     return true;
704   if ((flags & CC_DIGIT) && isdigit (c))
705     return true;
706   if ((flags & CC_PRINT) && isprint (c))
707     return true;
708   if ((flags & CC_PUNCT) && ispunct (c))
709     return true;    
710   if ((flags & CC_SPACE) && isspace (c))
711     return true;
712   if ((flags & CC_XDIGIT) && isxdigit (c))
713     return true;
714
715   if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
716     return true;
717   if ((flags & CC_NEWLINE) && c == '\n')
718     return true;
719   if ((flags & CC_CR) && c == '\r')
720     return true;
721
722   if ((flags & CC_BACKSLASH) && c == '\\')
723     return true;
724   if ((flags & CC_UNDERBAR) && c == '_')
725     return true;
726   if ((flags & CC_DASH) && c == '-')
727     return true;
728   if ((flags & CC_DOT) && c == '.')
729     return true;
730   if ((flags & CC_COMMA) && c == ',')
731     return true;
732   if ((flags & CC_COLON) && c == ':')
733     return true;
734   if ((flags & CC_SLASH) && c == '/')
735     return true;
736   if ((flags & CC_SINGLE_QUOTE) && c == '\'')
737     return true;
738   if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
739     return true;
740   if ((flags & CC_REVERSE_QUOTE) && c == '`')
741     return true;
742   if ((flags & CC_AT) && c == '@')
743     return true;
744   if ((flags & CC_EQUAL) && c == '=')
745     return true;
746
747   return false;
748 }
749
750 static inline bool
751 char_inc_exc (const char c, const unsigned int inclusive, const unsigned int exclusive)
752 {
753   return char_class (c, inclusive) && !char_class (c, exclusive);
754 }
755
756 bool
757 string_class (const char *str, const unsigned int inclusive, const unsigned int exclusive)
758 {
759   char c;
760   ASSERT (str);
761   while ((c = *str++))
762     {
763       if (!char_inc_exc (c, inclusive, exclusive))
764         return false;
765     }
766   return true;
767 }
768
769 /*
770  * Modify string in place.
771  * Guaranteed to not increase string length.
772  */
773 bool
774 string_mod (char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
775 {
776   const char *in = str;
777   bool ret = true;
778
779   ASSERT (str);
780
781   while (true)
782     {
783       char c = *in++;
784       if (c)
785         {
786           if (!char_inc_exc (c, inclusive, exclusive))
787             {
788               c = replace;
789               ret = false;
790             }
791           if (c)
792             *str++ = c;
793         }
794       else
795         {
796           *str = '\0';
797           break;
798         }
799     }
800   return ret;
801 }
802
803 const char *
804 string_mod_const (const char *str,
805                   const unsigned int inclusive,
806                   const unsigned int exclusive,
807                   const char replace,
808                   struct gc_arena *gc)
809 {
810   if (str)
811     {
812       char *buf = string_alloc (str, gc);
813       string_mod (buf, inclusive, exclusive, replace);
814       return buf;
815     }
816   else
817     return NULL;
818 }
819
820 void
821 string_replace_leading (char *str, const char match, const char replace)
822 {
823   ASSERT (match != '\0');
824   while (*str)
825     {
826       if (*str == match)
827         *str = replace;
828       else
829         break;
830       ++str;
831     }
832 }
833
834 #ifdef CHARACTER_CLASS_DEBUG
835
836 #define CC_INCLUDE    (CC_PRINT)
837 #define CC_EXCLUDE    (0)
838 #define CC_REPLACE    ('.')
839
840 void
841 character_class_debug (void)
842 {
843   char buf[256];
844
845   while (fgets (buf, sizeof (buf), stdin) != NULL)
846     {
847       string_mod (buf, CC_INCLUDE, CC_EXCLUDE, CC_REPLACE);
848       printf ("%s", buf);
849     }
850 }
851
852 #endif
853
854 #ifdef VERIFY_ALIGNMENT
855 void
856 valign4 (const struct buffer *buf, const char *file, const int line)
857 {
858   if (buf && buf->len)
859     {
860       int msglevel = D_ALIGN_DEBUG;
861       const unsigned int u = (unsigned int) BPTR (buf);
862
863       if (u & (PAYLOAD_ALIGN-1))
864         msglevel = D_ALIGN_ERRORS;
865
866       msg (msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
867            (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "",
868            file,
869            line,
870            (ptr_type)buf->data,
871            buf->offset,
872            buf->len,
873            buf->capacity,
874            buf_debug_file (buf),
875            buf_debug_line (buf));
876     }
877 }
878 #endif
879
880 /*
881  * struct buffer_list
882  */
883
884 #ifdef ENABLE_BUFFER_LIST
885
886 struct buffer_list *
887 buffer_list_new (const int max_size)
888 {
889   struct buffer_list *ret;
890   ALLOC_OBJ_CLEAR (ret, struct buffer_list);
891   ret->max_size = max_size;
892   ret->size = 0;
893   return ret;
894 }
895
896 void
897 buffer_list_free (struct buffer_list *ol)
898 {
899   buffer_list_reset (ol);
900   free (ol);
901 }
902
903 bool
904 buffer_list_defined (const struct buffer_list *ol)
905 {
906   return ol->head != NULL;
907 }
908
909 void
910 buffer_list_reset (struct buffer_list *ol)
911 {
912   struct buffer_entry *e = ol->head;
913   while (e)
914     {
915       struct buffer_entry *next = e->next;
916       free_buf (&e->buf);
917       free (e);
918       e = next;
919     }
920   ol->head = ol->tail = NULL;
921   ol->size = 0;
922 }
923
924 void
925 buffer_list_push (struct buffer_list *ol, const unsigned char *str)
926 {
927   if (!ol->max_size || ol->size < ol->max_size)
928     {
929       struct buffer_entry *e;
930       ALLOC_OBJ_CLEAR (e, struct buffer_entry);
931
932       ++ol->size;
933       if (ol->tail)
934         {
935           ASSERT (ol->head);
936           ol->tail->next = e;
937         }
938       else
939         {
940           ASSERT (!ol->head);
941           ol->head = e;
942         }
943       e->buf = string_alloc_buf ((const char *) str, NULL);
944       ol->tail = e;
945     }
946 }
947
948 const struct buffer *
949 buffer_list_peek (struct buffer_list *ol)
950 {
951   if (ol->head)
952     return &ol->head->buf;
953   else
954     return NULL;
955 }
956
957 static void
958 buffer_list_pop (struct buffer_list *ol)
959 {
960   if (ol->head)
961     {
962       struct buffer_entry *e = ol->head->next;
963       free_buf (&ol->head->buf);
964       free (ol->head);
965       ol->head = e;
966       --ol->size;
967       if (!e)
968         ol->tail = NULL;
969     }
970 }
971
972 void
973 buffer_list_advance (struct buffer_list *ol, int n)
974 {
975   if (ol->head)
976     {
977       struct buffer *buf = &ol->head->buf;
978       ASSERT (buf_advance (buf, n));
979       if (!BLEN (buf))
980         buffer_list_pop (ol);
981     }
982 }
983
984 struct buffer_list *
985 buffer_list_file (const char *fn, int max_line_len)
986 {
987   FILE *fp = fopen (fn, "r");
988   struct buffer_list *bl = NULL;
989
990   if (fp)
991     {
992       char *line = (char *) malloc (max_line_len);
993       if (line)
994         {
995           bl = buffer_list_new (0);
996           while (fgets (line, max_line_len, fp) != NULL)
997             buffer_list_push (bl, (unsigned char *)line);
998           free (line);
999         }
1000       fclose (fp);
1001     }
1002   return bl;
1003 }
1004
1005 #endif