1 /* vifm
2 * Copyright (C) 2012 xaizek.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "text_buffer.h"
20
21 #include <stdarg.h> /* va_list va_copy() va_start() va_end() */
22 #include <stddef.h> /* NULL size_t */
23 #include <stdio.h> /* vsnprintf() vsprintf() */
24 #include <stdlib.h> /* calloc() free() realloc() */
25 #include <string.h> /* strcpy() */
26
27 /* Text buffer type. */
28 struct vle_textbuf
29 {
30 size_t capacity; /* Total amount of available space. */
31 size_t len; /* Total amount of used space. */
32 char *data; /* Allocated piece of memory. */
33 int need_line_break; /* Need to put line break before newly appended data. */
34 };
35
36 static void appendfv(vle_textbuf *tb, int line, const char format[],
37 va_list ap);
38 static void ensure_allocated(vle_textbuf *tb, size_t extra);
39
40 /* Definition of predefined buffer for collecting errors of the engine. */
41 static vle_textbuf vle_err_data;
42 vle_textbuf *const vle_err = &vle_err_data;
43
44 vle_textbuf *
vle_tb_create()45 vle_tb_create()
46 {
47 return calloc(1, sizeof(vle_textbuf));
48 }
49
50 void
vle_tb_free(vle_textbuf * tb)51 vle_tb_free(vle_textbuf *tb)
52 {
53 if(tb != NULL)
54 {
55 vle_tb_clear(tb);
56 free(tb);
57 }
58 }
59
60 char *
vle_tb_release(vle_textbuf * tb)61 vle_tb_release(vle_textbuf *tb)
62 {
63 char *const data = (tb->data != NULL) ? tb->data : strdup("");
64 free(tb);
65 return data;
66 }
67
68 void
vle_tb_clear(vle_textbuf * tb)69 vle_tb_clear(vle_textbuf *tb)
70 {
71 if(tb->data != NULL)
72 {
73 free(tb->data);
74 tb->data = 0;
75 tb->capacity = 0;
76 tb->len = 0;
77 tb->need_line_break = 0;
78 }
79 }
80
81 void
vle_tb_append(vle_textbuf * tb,const char str[])82 vle_tb_append(vle_textbuf *tb, const char str[])
83 {
84 vle_tb_appendf(tb, "%s", str);
85 }
86
87 void
vle_tb_appendf(vle_textbuf * tb,const char format[],...)88 vle_tb_appendf(vle_textbuf *tb, const char format[], ...)
89 {
90 va_list ap;
91 va_start(ap, format);
92 appendfv(tb, 0, format, ap);
93 va_end(ap);
94 }
95
96 void
vle_tb_append_line(vle_textbuf * tb,const char str[])97 vle_tb_append_line(vle_textbuf *tb, const char str[])
98 {
99 vle_tb_append_linef(tb, "%s", str);
100 }
101
102 void
vle_tb_append_linef(vle_textbuf * tb,const char format[],...)103 vle_tb_append_linef(vle_textbuf *tb, const char format[], ...)
104 {
105 va_list ap;
106 va_start(ap, format);
107 appendfv(tb, 1, format, ap);
108 va_end(ap);
109 }
110
111 /* Appends formatted string to the buffer. New data is either a string or a
112 * line (string that terminates with new line character).*/
113 static void
appendfv(vle_textbuf * tb,int line,const char format[],va_list ap)114 appendfv(vle_textbuf *tb, int line, const char format[], va_list ap)
115 {
116 va_list aq;
117 size_t needed_size;
118
119 va_copy(aq, ap);
120 needed_size = (tb->len != 0) + vsnprintf(tb->data, 0, format, ap);
121
122 ensure_allocated(tb, needed_size);
123 if(tb->need_line_break && tb->len != 0 && tb->capacity > tb->len)
124 {
125 strcpy(&tb->data[tb->len++], "\n");
126 tb->need_line_break = 0;
127 }
128
129 tb->len += vsnprintf(tb->data + tb->len, tb->capacity - tb->len, format, aq);
130 va_end(aq);
131
132 tb->need_line_break = line;
133 }
134
135 /* Ensures that buffer has at least specified amount of bytes after its end. */
136 static void
ensure_allocated(vle_textbuf * tb,size_t extra)137 ensure_allocated(vle_textbuf *tb, size_t extra)
138 {
139 const size_t needed_size = tb->len + extra + 1;
140 if(tb->capacity < needed_size)
141 {
142 char *const ptr = realloc(tb->data, needed_size);
143 if(ptr != NULL)
144 {
145 tb->data = ptr;
146 tb->capacity = needed_size;
147 }
148 }
149 }
150
151 const char *
vle_tb_get_data(vle_textbuf * tb)152 vle_tb_get_data(vle_textbuf *tb)
153 {
154 return (tb->data == NULL) ? "" : tb->data;
155 }
156
157 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
158 /* vim: set cinoptions+=t0 filetype=c : */
159