source: mainline/uspace/dist/src/c/demos/edit/sheet.c@ a35b458

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 8.5 KB
Line 
1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup edit
30 * @{
31 */
32/**
33 * @file
34 * @brief Prototype implementation of Sheet data structure.
35 *
36 * The sheet is an abstract data structure representing a piece of text.
37 * On top of this data structure we can implement a text editor. It is
38 * possible to implement the sheet such that the editor can make small
39 * changes to large files or files containing long lines efficiently.
40 *
41 * The sheet structure allows basic operations of text insertion, deletion,
42 * retrieval and mapping of coordinates to position in the file and vice
43 * versa. The text that is inserted or deleted can contain tabs and newlines
44 * which are interpreted and properly acted upon.
45 *
46 * This is a trivial implementation with poor efficiency with O(N+n)
47 * insertion and deletion and O(N) mapping (in both directions), where
48 * N is the size of the file and n is the size of the inserted/deleted text.
49 */
50
51#include <stdlib.h>
52#include <str.h>
53#include <errno.h>
54#include <adt/list.h>
55#include <align.h>
56#include <macros.h>
57
58#include "sheet.h"
59#include "sheet_impl.h"
60
61enum {
62 TAB_WIDTH = 8,
63
64 /** Initial of dat buffer in bytes */
65 INITIAL_SIZE = 32
66};
67
68/** Initialize an empty sheet. */
69errno_t sheet_create(sheet_t **rsh)
70{
71 sheet_t *sh;
72
73 sh = calloc(1, sizeof(sheet_t));
74 if (sh == NULL)
75 return ENOMEM;
76
77 sh->dbuf_size = INITIAL_SIZE;
78 sh->text_size = 0;
79
80 sh->data = malloc(sh->dbuf_size);
81 if (sh->data == NULL)
82 return ENOMEM;
83
84 list_initialize(&sh->tags);
85
86 *rsh = sh;
87 return EOK;
88}
89
90/** Insert text into sheet.
91 *
92 * @param sh Sheet to insert to.
93 * @param pos Point where to insert.
94 * @param dir Whether to insert before or after the point (affects tags).
95 * @param str The text to insert (printable characters, tabs, newlines).
96 *
97 * @return EOK on success or an error code.
98 *
99 * @note @a dir affects which way tags that were placed on @a pos will
100 * move. If @a dir is @c dir_before, the tags will move forward
101 * and vice versa.
102 */
103errno_t sheet_insert(sheet_t *sh, spt_t *pos, enum dir_spec dir, char *str)
104{
105 char *ipp;
106 size_t sz;
107 char *newp;
108
109 sz = str_size(str);
110 if (sh->text_size + sz > sh->dbuf_size) {
111 /* Enlarge data buffer. */
112 newp = realloc(sh->data, sh->dbuf_size * 2);
113 if (newp == NULL)
114 return ELIMIT;
115
116 sh->data = newp;
117 sh->dbuf_size = sh->dbuf_size * 2;
118 }
119
120 ipp = sh->data + pos->b_off;
121
122 /* Copy data. */
123 memmove(ipp + sz, ipp, sh->text_size - pos->b_off);
124 memcpy(ipp, str, sz);
125 sh->text_size += sz;
126
127 /* Adjust tags. */
128
129 list_foreach(sh->tags, link, tag_t, tag) {
130 if (tag->b_off > pos->b_off)
131 tag->b_off += sz;
132 else if (tag->b_off == pos->b_off && dir == dir_before)
133 tag->b_off += sz;
134 }
135
136 return EOK;
137}
138
139/** Delete text from sheet.
140 *
141 * Deletes the range of text between two points from the sheet.
142 *
143 * @param sh Sheet to delete from.
144 * @param spos Starting point.
145 * @param epos Ending point.
146 *
147 * @return EOK on success or an error code.
148 **/
149errno_t sheet_delete(sheet_t *sh, spt_t *spos, spt_t *epos)
150{
151 char *spp;
152 size_t sz;
153 char *newp;
154 size_t shrink_size;
155
156 spp = sh->data + spos->b_off;
157 sz = epos->b_off - spos->b_off;
158
159 memmove(spp, spp + sz, sh->text_size - (spos->b_off + sz));
160 sh->text_size -= sz;
161
162 /* Adjust tags. */
163 list_foreach(sh->tags, link, tag_t, tag) {
164 if (tag->b_off >= epos->b_off)
165 tag->b_off -= sz;
166 else if (tag->b_off >= spos->b_off)
167 tag->b_off = spos->b_off;
168 }
169
170 /* See if we should free up some memory. */
171 shrink_size = max(sh->dbuf_size / 4, INITIAL_SIZE);
172 if (sh->text_size <= shrink_size && sh->dbuf_size > INITIAL_SIZE) {
173 /* Shrink data buffer. */
174 newp = realloc(sh->data, shrink_size);
175 if (newp == NULL) {
176 /* Failed to shrink buffer... no matter. */
177 return EOK;
178 }
179
180 sh->data = newp;
181 sh->dbuf_size = shrink_size;
182 }
183
184 return EOK;
185}
186
187/** Read text from sheet. */
188void sheet_copy_out(sheet_t *sh, spt_t const *spos, spt_t const *epos,
189 char *buf, size_t bufsize, spt_t *fpos)
190{
191 char *spp;
192 size_t range_sz;
193 size_t copy_sz;
194 size_t off, prev;
195 wchar_t c;
196
197 spp = sh->data + spos->b_off;
198 range_sz = epos->b_off - spos->b_off;
199 copy_sz = (range_sz < bufsize - 1) ? range_sz : bufsize - 1;
200
201 prev = off = 0;
202 do {
203 prev = off;
204 c = str_decode(spp, &off, copy_sz);
205 } while (c != '\0');
206
207 /* Crop copy_sz down to the last full character. */
208 copy_sz = prev;
209
210 memcpy(buf, spp, copy_sz);
211 buf[copy_sz] = '\0';
212
213 fpos->b_off = spos->b_off + copy_sz;
214 fpos->sh = sh;
215}
216
217/** Get point preceding or following character cell. */
218void sheet_get_cell_pt(sheet_t *sh, coord_t const *coord, enum dir_spec dir,
219 spt_t *pt)
220{
221 size_t cur_pos, prev_pos;
222 wchar_t c;
223 coord_t cc;
224
225 cc.row = cc.column = 1;
226 cur_pos = prev_pos = 0;
227 while (true) {
228 if (prev_pos >= sh->text_size) {
229 /* Cannot advance any further. */
230 break;
231 }
232
233 if ((cc.row >= coord->row && cc.column > coord->column) ||
234 cc.row > coord->row) {
235 /* We are past the requested coordinates. */
236 break;
237 }
238
239 prev_pos = cur_pos;
240
241 c = str_decode(sh->data, &cur_pos, sh->text_size);
242 if (c == '\n') {
243 ++cc.row;
244 cc.column = 1;
245 } else if (c == '\t') {
246 cc.column = 1 + ALIGN_UP(cc.column, TAB_WIDTH);
247 } else {
248 ++cc.column;
249 }
250 }
251
252 pt->sh = sh;
253 pt->b_off = (dir == dir_before) ? prev_pos : cur_pos;
254}
255
256/** Get the number of character cells a row occupies. */
257void sheet_get_row_width(sheet_t *sh, int row, int *length)
258{
259 coord_t coord;
260 spt_t pt;
261
262 /* Especially nasty hack */
263 coord.row = row;
264 coord.column = 65536;
265
266 sheet_get_cell_pt(sh, &coord, dir_before, &pt);
267 spt_get_coord(&pt, &coord);
268 *length = coord.column;
269}
270
271/** Get the number of rows in a sheet. */
272void sheet_get_num_rows(sheet_t *sh, int *rows)
273{
274 int cnt;
275 size_t bo;
276
277 cnt = 1;
278 for (bo = 0; bo < sh->dbuf_size; ++bo) {
279 if (sh->data[bo] == '\n')
280 cnt += 1;
281 }
282
283 *rows = cnt;
284}
285
286/** Get the coordinates of an s-point. */
287void spt_get_coord(spt_t const *pos, coord_t *coord)
288{
289 size_t off;
290 coord_t cc;
291 wchar_t c;
292 sheet_t *sh;
293
294 sh = pos->sh;
295 cc.row = cc.column = 1;
296
297 off = 0;
298 while (off < pos->b_off && off < sh->text_size) {
299 c = str_decode(sh->data, &off, sh->text_size);
300 if (c == '\n') {
301 ++cc.row;
302 cc.column = 1;
303 } else if (c == '\t') {
304 cc.column = 1 + ALIGN_UP(cc.column, TAB_WIDTH);
305 } else {
306 ++cc.column;
307 }
308 }
309
310 *coord = cc;
311}
312
313/** Test if two s-points are equal. */
314bool spt_equal(spt_t const *a, spt_t const *b)
315{
316 return a->b_off == b->b_off;
317}
318
319/** Get a character at spt and return next spt */
320wchar_t spt_next_char(spt_t spt, spt_t *next)
321{
322 wchar_t ch = str_decode(spt.sh->data, &spt.b_off, spt.sh->text_size);
323 if (next)
324 *next = spt;
325 return ch;
326}
327
328wchar_t spt_prev_char(spt_t spt, spt_t *prev)
329{
330 wchar_t ch = str_decode_reverse(spt.sh->data, &spt.b_off, spt.sh->text_size);
331 if (prev)
332 *prev = spt;
333 return ch;
334}
335
336/** Place a tag on the specified s-point. */
337void sheet_place_tag(sheet_t *sh, spt_t const *pt, tag_t *tag)
338{
339 tag->b_off = pt->b_off;
340 tag->sh = sh;
341 list_append(&tag->link, &sh->tags);
342}
343
344/** Remove a tag from the sheet. */
345void sheet_remove_tag(sheet_t *sh, tag_t *tag)
346{
347 list_remove(&tag->link);
348}
349
350/** Get s-point on which the tag is located right now. */
351void tag_get_pt(tag_t const *tag, spt_t *pt)
352{
353 pt->b_off = tag->b_off;
354 pt->sh = tag->sh;
355}
356
357/** @}
358 */
Note: See TracBrowser for help on using the repository browser.