summaryrefslogtreecommitdiff
path: root/libmailutils/locus/linetrack.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/locus/linetrack.c')
-rw-r--r--libmailutils/locus/linetrack.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/libmailutils/locus/linetrack.c b/libmailutils/locus/linetrack.c
new file mode 100644
index 000000000..86a3dd823
--- /dev/null
+++ b/libmailutils/locus/linetrack.c
@@ -0,0 +1,268 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <mailutils/types.h>
+#include <mailutils/locus.h>
+#include <mailutils/error.h>
+
+/* The line-tracker structure keeps track of the last N lines read from a
+ text input file. For each line read it keeps the number of characters
+ in that line including the newline. This information is stored in a
+ syclic stack of N elements. Top of stack always represents the current
+ line. For the purpose of line tracker, current line is the line that is
+ being visited, such that its final newline character has not yet been
+ seen. Once the newline is seen, the line is pushed on stack, and a new
+ current line is assumed.
+
+ The value of N must not be less than 2.
+*/
+struct mu_linetrack
+{
+ char const *file_name; /* Name of the source file */
+ size_t max_lines; /* Max. number of lines history kept by tracker (N) */
+ size_t head; /* Index of the eldest element on stack */
+ size_t tos; /* Index of the most recent element on stack
+ (< max_lines) */
+ unsigned hline; /* Number of line corresponding to cols[head] */
+ unsigned *cols; /* Cyclic stack or character counts.
+ Number of characters in line (hline + n) is
+ cols[head + n] (0 <= n <= tos). */
+};
+
+static inline unsigned *
+cols_ptr (mu_linetrack_t trk, size_t n)
+{
+ return &trk->cols[(trk->head + n) % trk->max_lines];
+}
+
+static inline unsigned *
+cols_tos_ptr (mu_linetrack_t trk)
+{
+ return cols_ptr (trk, trk->tos);
+}
+
+static inline unsigned
+cols_peek (mu_linetrack_t trk, size_t n)
+{
+ return *cols_ptr (trk, n);
+}
+
+int
+mu_linetrack_create (mu_linetrack_t *ret,
+ char const *file_name, size_t max_lines)
+{
+ int rc;
+ struct mu_linetrack *trk;
+
+ trk = malloc (sizeof *trk);
+ if (!trk)
+ return errno;
+ trk->cols = calloc (max_lines, sizeof (trk->cols[0]));
+ if (!trk->cols)
+ {
+ rc = errno;
+ free (trk);
+ return rc;
+ }
+ rc = mu_ident_ref (file_name, &trk->file_name);
+ if (rc)
+ {
+ free (trk->cols);
+ free (trk);
+ return rc;
+ }
+
+ if (max_lines < 2)
+ max_lines = 2;
+ trk->max_lines = max_lines;
+ trk->head = 0;
+ trk->tos = 0;
+ trk->hline = 1;
+ trk->cols[0] = 0;
+
+ *ret = trk;
+ return 0;
+}
+
+int
+mu_linetrack_rebase (mu_linetrack_t trk, struct mu_locus_point const *pt)
+{
+ char const *file_name;
+ int rc = mu_ident_ref (pt->mu_file, &file_name);
+ if (rc)
+ return rc;
+ mu_ident_deref (trk->file_name);
+ trk->file_name = file_name;
+ trk->hline = pt->mu_line;
+ *cols_ptr (trk, 0) = pt->mu_col;
+ return 0;
+}
+
+void
+mu_linetrack_free (mu_linetrack_t trk)
+{
+ if (trk)
+ {
+ mu_ident_deref (trk->file_name);
+ free (trk->cols);
+ free (trk);
+ }
+}
+
+void
+mu_linetrack_destroy (mu_linetrack_t *trk)
+{
+ if (trk)
+ {
+ mu_linetrack_free (*trk);
+ *trk = NULL;
+ }
+}
+
+static inline unsigned *
+push (mu_linetrack_t trk)
+{
+ unsigned *ptr;
+ if (trk->tos == trk->max_lines - 1)
+ {
+ trk->head++;
+ trk->hline++;
+ }
+ else
+ trk->tos++;
+ *(ptr = cols_tos_ptr (trk)) = 0;
+ return ptr;
+}
+
+static inline unsigned *
+pop (mu_linetrack_t trk)
+{
+ if (trk->tos == 0)
+ return NULL;
+ trk->tos--;
+ return cols_tos_ptr (trk);
+}
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~((size_t)0))
+#endif
+
+int
+mu_linetrack_stat (struct mu_linetrack *trk, struct mu_linetrack_stat *st)
+{
+ size_t i, nch = 0;
+
+ for (i = 0; i <= trk->tos; i++)
+ {
+ unsigned n = cols_peek (trk, i);
+ if (SIZE_MAX - nch < n)
+ return ERANGE;
+ nch += n;
+ }
+
+ st->start_line = trk->hline;
+ st->n_lines = trk->tos + 1;
+ st->n_chars = nch;
+
+ return 0;
+}
+
+int
+mu_linetrack_at_bol (struct mu_linetrack *trk)
+{
+ return *cols_tos_ptr (trk) == 0;
+}
+
+void
+mu_linetrack_advance (struct mu_linetrack *trk,
+ struct mu_locus_range *loc,
+ char const *text, size_t leng)
+{
+ unsigned *ptr;
+
+ if (text == NULL || leng == 0)
+ return;
+
+ mu_locus_point_set_file (&loc->beg, trk->file_name);
+ mu_locus_point_set_file (&loc->end, trk->file_name);
+ loc->beg.mu_line = trk->hline + trk->tos;
+ ptr = cols_tos_ptr (trk);
+ loc->beg.mu_col = *ptr + 1;
+ while (leng--)
+ {
+ (*ptr)++;
+ if (*text == '\n')
+ ptr = push (trk);
+ text++;
+ }
+ if (*ptr)
+ {
+ loc->end.mu_line = trk->hline + trk->tos;
+ loc->end.mu_col = *ptr;
+ }
+ else
+ {
+ /* Text ends with a newline. Keep the previous line number. */
+ loc->end.mu_line = trk->hline + trk->tos - 1;
+ loc->end.mu_col = cols_peek (trk, trk->tos - 1) - 1;
+ if (loc->end.mu_col + 1 == loc->beg.mu_col)
+ {
+ /* This happens if the previous line contained only newline. */
+ loc->beg.mu_col = loc->end.mu_col;
+ }
+ }
+}
+
+int
+mu_linetrack_locus (struct mu_linetrack *trk, struct mu_locus_point *lp)
+{
+ lp->mu_line = trk->hline + trk->tos;
+ return mu_locus_point_set_file (lp, trk->file_name);
+}
+
+int
+mu_linetrack_retreat (struct mu_linetrack *trk, size_t n)
+{
+ struct mu_linetrack_stat st;
+
+ mu_linetrack_stat (trk, &st);
+ if (n > st.n_chars)
+ return ERANGE;
+ else
+ {
+ unsigned *ptr = cols_tos_ptr (trk);
+ while (n--)
+ {
+ if (*ptr == 0)
+ {
+ ptr = pop (trk);
+ if (!ptr || *ptr == 0)
+ {
+ mu_error ("%s:%d: INTERNAL ERROR: out of pop back\n",
+ __FILE__, __LINE__);
+ return ERANGE;
+ }
+ }
+ --*ptr;
+ }
+ }
+ return 0;
+}
+
+

Return to:

Send suggestions and report system problems to the System administrator.