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