From 5257358958cca6a8c44aed0a03aa962bcf4563a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonathan=20Sch=C3=B6bel?= <jonathan@xn--schbel-yxa.info>
Date: Sun, 2 Jul 2023 12:31:07 +0200
Subject: [PATCH] Text: added stupid replacement function

The copy_and_replace function replaces a single character with a string,
while copying. This may be replaced by an elaborate function as
manipulating a text normally means that manipulating is deferred until
needed, which this function contradicts to.
---
 sefht.geany                  |  10 +--
 src/lib/sefht/text.c         |  31 +++++++
 src/lib/sefht/text.h         |  11 +++
 src/lib/sefht/text_segment.c | 155 +++++++++++++++++++++++++++++++++++
 tests/test_text.c            |  28 +++++++
 5 files changed, 230 insertions(+), 5 deletions(-)

diff --git a/sefht.geany b/sefht.geany
index f42fa15..59edf8e 100644
--- a/sefht.geany
+++ b/sefht.geany
@@ -28,7 +28,7 @@ long_line_behaviour=1
 long_line_column=72
 
 [files]
-current_page=25
+current_page=45
 FILE_NAME_0=139;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2FREADME;0;8
 FILE_NAME_1=134;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2F.gitignore;0;8
 FILE_NAME_2=1737;Sh;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fconfigure.ac;0;8
@@ -51,10 +51,10 @@ FILE_NAME_18=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprg
 FILE_NAME_19=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr.h;0;8
 FILE_NAME_20=26;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr_static.c;0;8
 FILE_NAME_21=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Fattr_data.h;0;8
-FILE_NAME_22=2765;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.c;0;8
-FILE_NAME_23=19;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.h;0;8
+FILE_NAME_22=2929;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.c;0;8
+FILE_NAME_23=2065;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext.h;0;8
 FILE_NAME_24=1036;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_data.h;0;8
-FILE_NAME_25=4650;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.c;0;8
+FILE_NAME_25=8479;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.c;0;8
 FILE_NAME_26=1083;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment.h;0;8
 FILE_NAME_27=900;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_segment_mark.c;0;8
 FILE_NAME_28=1867;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Fsrc%2Flib%2Fsefht%2Ftext_mark_static.c;0;8
@@ -74,7 +74,7 @@ FILE_NAME_41=8232;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fp
 FILE_NAME_42=33;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_node_fragment.c;0;8
 FILE_NAME_43=4771;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_fragment.c;0;8
 FILE_NAME_44=24;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_attr.c;0;8
-FILE_NAME_45=3584;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8
+FILE_NAME_45=4221;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text.c;0;8
 FILE_NAME_46=994;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_text_mark.c;0;8
 FILE_NAME_47=29;C;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftests%2Ftest_validator.c;0;8
 FILE_NAME_48=536;None;0;EUTF-8;1;1;0;%2Fhome%2Fjonathan%2FDokumente%2Fprojekte%2Fprgm%2Finternet%2Fweb%2FSeFHT%2Ftodo.txt;0;8
diff --git a/src/lib/sefht/text.c b/src/lib/sefht/text.c
index 5b97cb0..988efb4 100644
--- a/src/lib/sefht/text.c
+++ b/src/lib/sefht/text.c
@@ -144,6 +144,37 @@ SH_Text_copy (const struct SH_Text * text,
 	return copy;
 }
 
+/*@null@*/
+/*@only@*/
+struct SH_Text *
+SH_Text_copy_and_replace (const struct SH_Text * text,
+                          char old, char * new,
+                          /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	struct SH_Text * copy;
+
+	copy = malloc (sizeof (struct SH_Text));
+	if (NULL == copy)
+	{
+		set_status (status, E_ALLOC, 3, "malloc failed");
+		return NULL;
+	}
+
+	copy->data = copy_and_replace_text_segment (text->data,
+	                                            old, new, status);
+	if (NULL == copy->data)
+	{
+		free (copy);
+		return NULL;
+	}
+
+	set_success (status);
+	return copy;
+}
+
 size_t
 SH_Text_get_length (const struct SH_Text * text,
                     /*@null@*/ /*@out@*/ struct SH_Status * status)
diff --git a/src/lib/sefht/text.h b/src/lib/sefht/text.h
index 59e3173..278fccf 100644
--- a/src/lib/sefht/text.h
+++ b/src/lib/sefht/text.h
@@ -68,6 +68,17 @@ SH_Text_copy (const SH_Text * text,
 	/*@modifies fileSystem@*/
 	/*@modifies status@*/;
 
+
+/*@null@*/
+/*@only@*/
+SH_Text *
+SH_Text_copy_and_replace (const SH_Text * text,
+                          char old, char * new,
+                          /*@null@*/ /*@out@*/ struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/;
+
 size_t
 SH_Text_get_length (const struct SH_Text * text,
                     /*@null@*/ /*@out@*/ struct SH_Status * status)
diff --git a/src/lib/sefht/text_segment.c b/src/lib/sefht/text_segment.c
index 0d911cd..4cda050 100644
--- a/src/lib/sefht/text_segment.c
+++ b/src/lib/sefht/text_segment.c
@@ -367,3 +367,158 @@ squash_text_segment (const struct text_segment * segment,
 		copy_seg->next = NULL;
 	}
 }
+
+static inline
+/*@null@*/
+/*@only@*/
+struct text_segment *
+copy_and_replace_text_segment (const struct text_segment * segment,
+                               char old, char * new,
+                               /*@null@*/ /*@out@*/
+                               struct SH_Status * status)
+	/*@globals fileSystem@*/
+	/*@modifies fileSystem@*/
+	/*@modifies status@*/
+{
+	const struct text_segment * start;
+	const struct text_segment * end;
+	struct text_segment * copy;
+	struct text_segment * copy_seg;
+
+
+	copy = malloc (sizeof (struct text_segment));
+	if (NULL == copy)
+	{
+		set_status (status, E_ALLOC, 3, "malloc failed");
+		return NULL;
+	}
+
+	copy->next = NULL;
+
+	/* copy_seg is the segment we're currently copying to */
+	copy_seg = copy;
+
+	/* start to end are the segments we're copying from. */
+	/* start points to the first segment to copy.
+	 * end points to the first segment not being copied in
+	 * the current iteration. */
+	end = segment;
+	start = end;
+
+	/* At the beginning of each iteration start == end.
+	 * This is ensured before the loop and at the end of the outer
+	 * loop with the last for loop, which does the actual copying.
+	 *
+	 * Then end is adjusted to point after the last segment to be
+	 * copied. If it isn't NULL afterwards another loop iteration
+	 * has to take place.
+	 *
+	 * With start and end properly set, and the length and size of
+	 * the resulting segment determined, the copying can start,
+	 * which is just a repeated string concatenation.
+	 *
+	 * If another iteration is following, the next segment has to be
+	 * allocated. This can't be done at the beginning, because
+	 * before the first iteration the pointer to the allocated
+	 * segment must be written to copy directly to create the
+	 * anchor of the linked list. (So there is no ?->next
+	 * to write to.)
+	 * Actually copy_seg could be typed (struct text_segment **)
+	 * and then be set to &(copy) before the loop and to
+	 * &((*copy_seg)->next) after each iteration, but that would
+	 * complicate things with another indirection...
+	 * Implementing the loop as for loop and writing the allocation
+	 * code into their head is also not possible, because compound
+	 * statements aren't allowed there.
+	 * Thus the allocation has to take place at the end of the loop. */
+	while (TRUE)
+	{
+		char * text;
+		size_t r_length;
+
+		/* calculate length, adjust end */
+		for (copy_seg->length = 0; end != NULL; end = end->next)
+		{
+			/* catch overflow, use <= instead of <
+			 * to prevent overflow of copy_seg->size */
+			if (SIZE_MAX - end->length <= copy_seg->length)
+			{
+				/* stop copying */
+				end = end->next;
+
+				/*@innerbreak@*/
+				break;
+			}
+
+			copy_seg->length += end->length;
+		}
+
+		/* initialize marks
+		 * TODO: copy and adjust the marks regarding to
+		 * the concatenated segment */
+		init_marks (copy_seg);
+
+		/* This addition can not overflow, because in the
+		 * for-loop it was tested with > instead of >= which
+		 * would be appropriate. See comment there. */
+		copy_seg->size = get_alloc_size (copy_seg->length + 1);
+
+		copy_seg->text = malloc (copy_seg->size);
+		if (NULL == copy_seg->text)
+		{
+			set_status (status, E_ALLOC, 3, "malloc failed");
+			free_text_segment (copy);
+			return NULL;
+		}
+
+
+		text = copy_seg->text;
+		r_length = strlen (new);
+		/* actual copying */
+		/* Initialization was done since the beginning of
+		 * the outer loop. So think of all this code standing
+		 *   |  here.
+		 *   v        */
+		for (; start != end; start = start->next)
+		{
+			char * s_start;
+			char * s_end;
+
+			s_start = start->text,
+			s_end = s_start + start->length;
+
+			while (s_end != s_start)
+			{
+				if (old == *s_start)
+				{
+					memcpy (text, new, r_length);
+					text += r_length;
+					copy_seg->length += r_length-1;
+					s_start++;
+				}
+				else
+				{
+					*text++ = *s_start++;
+				}
+			}
+		}
+		text[0] = '\0';
+
+		/* See comment of outer loop. (4th paragraph) */
+		if (end == NULL)
+		{
+			return copy;
+		}
+
+		copy_seg->next = malloc (sizeof (struct text_segment));
+		if (NULL == copy_seg->next)
+		{
+			set_status (status, E_ALLOC, 3, "malloc failed");
+			free_text_segment (copy);
+			return NULL;
+		}
+
+		copy_seg = copy_seg->next;
+		copy_seg->next = NULL;
+	}
+}
diff --git a/tests/test_text.c b/tests/test_text.c
index 4f03a78..247beb1 100644
--- a/tests/test_text.c
+++ b/tests/test_text.c
@@ -161,6 +161,33 @@ START_TEST (test_text_copy_with_status)
 }
 END_TEST
 
+START_TEST (test_text_copy_replace_with_status)
+{
+	struct SH_Status status;
+	struct SH_Text * text;
+	struct SH_Text * copy;
+
+	/* setup */
+	text = SH_Text_new_from_string ("Text12345", NULL);
+	ck_assert_ptr_ne (NULL, text);
+
+	/* test */
+	_status_preinit (status);
+	copy = SH_Text_copy_and_replace (text, '1', "000", &status);
+	ck_assert_int_eq (status.status, SUCCESS);
+	ck_assert_ptr_ne (copy, NULL);
+
+	ck_assert_ptr_ne (copy, text);
+	ck_assert_ptr_ne (copy->data->text, text->data->text);
+	ck_assert_str_ne (copy->data->text, text->data->text);
+	ck_assert_str_eq ("Text0002345", copy->data->text);
+
+	/* cleanup */
+	SH_Text_free (copy);
+	SH_Text_free (text);
+}
+END_TEST
+
 START_TEST(test_text_get_length_no_status)
 {
 	struct SH_Text * text;
@@ -671,6 +698,7 @@ Suite * text_suite (void)
 	tcase_add_test (tc_core, test_text_string_with_status);
 	tcase_add_test (tc_core, test_text_copy_no_status);
 	tcase_add_test (tc_core, test_text_copy_with_status);
+	tcase_add_test (tc_core, test_text_copy_replace_with_status);
 	tcase_add_test (tc_core, test_text_get_length_no_status);
 	tcase_add_test (tc_core, test_text_get_length_with_status);
 	tcase_add_test (tc_core, test_text_get_char_no_status);
-- 
GitLab