SDDSlib
Loading...
Searching...
No Matches
edit_string.c
Go to the documentation of this file.
1/**
2 * @file edit_string.c
3 * @brief Implements a simple single-line text-driven editor for user-directed data editing.
4 *
5 * This file provides functionalities to edit strings based on user-defined commands.
6 * It includes operations such as inserting, deleting, moving the cursor, searching, killing,
7 * and yanking text within a single line of text. The editor supports complex command sequences
8 * and maintains a kill buffer for temporary storage of deleted text.
9 *
10 * @copyright
11 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
12 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
13 *
14 * @license
15 * This file is distributed under the terms of the Software License Agreement
16 * found in the file LICENSE included with this distribution.
17 *
18 * @author M. Borland, C. Saunders, R. Soliday, H. Shang
19 */
20
21#include "mdb.h"
22#include <ctype.h>
23long edit_string(char *text, char *edit);
24
25#define DEBUG 0
26
27#define IS_WORD_END(c) ((c) == ' ' || (c) == '\t' || (c) == '_' || (c) == '-')
28
29/* edit commands:
30 * <n>(<seq>) perform command sequence <seq>, <n> times
31 * <n>d delete <n> characters
32 * <n>f forward <n> characters
33 * <n>b backward <n> characters
34 * <n>D delete <n> words
35 * <n>F forward <n> words
36 * <n>B backward <n> words
37 * a go to beginning of line
38 * e go to end of line
39 * <n>i{delim}text{delim} insert text <n> times
40 * r{delim}text{delim} reverse search for text, CP to end
41 * R{delim}text{delim} reverse search for text, CP to start
42 * r?{delim}text{delim} reverse search for text, CP to end, end edit if absent
43 * R?{delim}text{delim} reverse search for text, CP to start, end edit if absent
44 * s{delim}text{delim} search for text, CP to end
45 * S{delim}text{delim} search for text, CP to start
46 * s?{delim}text{delim} search for text, CP to end, end edit if absent
47 * S?{delim}text{delim} search for text, CP to start, end edit if absent
48 * <n>k kill <n> characters
49 * <n>K kill <n> words
50 * <n>z<c> kill up to <c>, <n> times
51 * <n>Z<c> kill up to and including <c>, <n> times
52 * x[-]{delim}text{delim} kill characters as long as they are [not] in text.
53 * c clear the kill buffer
54 * <n>y yank kill buffer
55 * <n>%[gh]{delim}text1{delim}text2{delim} replace text1 with text2 <n> times
56 * g=global, h=here (at CP) only
57 */
58
59typedef struct {
60 char *editPtr, *editText;
61 long count, pending;
63
64/**
65 * @brief Edits the provided text based on the specified edit commands.
66 *
67 * Processes a single line of text by applying a sequence of editing commands.
68 * Supports operations like inserting, deleting characters or words, moving the cursor,
69 * searching for substrings, and manipulating the kill buffer.
70 *
71 * @param text Pointer to the text string to be edited. The string is modified in place.
72 * @param edit Pointer to the string containing edit commands to apply to the text.
73 * @return Returns 1 on successful editing, or 0 if an error occurs (e.g., memory allocation failure).
74 */
75long edit_string(char *text, char *edit0) {
76 short count, new_kill;
77 char *ptr, *text_start, delimiter, *ptr1, *ptr2, *ptr3;
78 char *delimLoc, *editNext, *charList;
79 static char kill[4096], buffer[4096];
80 char *orig, *repl;
81 int global, conditional, conditionalReturn, here, i;
82 char *edit, cSave;
83 EDIT_SEQUENCE *editSeq;
84 int stackLevel, stackDepth, doPush, parenCount, invert;
85 size_t j;
86
87#if DEBUG
88 printf("text = %s\nedit = %s\n", text, edit0);
89#endif
90 text_start = text;
91 kill[0] = 0;
92 if (!(editSeq = malloc(sizeof(*editSeq) * (stackDepth = 10))))
93 return 0;
94 cp_str(&editSeq[0].editText, edit0);
95 editSeq[0].count = 1;
96 for (i = 0; i < stackDepth; i++)
97 editSeq[i].pending = 0;
98 editSeq[0].editPtr = NULL;
99 stackLevel = 0;
100
101 new_kill = 1;
102 while (stackLevel >= 0) {
103#if DEBUG
104 printf("stackLevel = %ld\n", stackLevel);
105#endif
106 while (editSeq[stackLevel].pending || editSeq[stackLevel].count--) {
107 if (!editSeq[stackLevel].pending)
108 editSeq[stackLevel].editPtr = editSeq[stackLevel].editText;
109 editSeq[stackLevel].pending = 0;
110 if (!editSeq[stackLevel].editPtr)
111 continue;
112#if DEBUG
113 printf("Count = %ld\n", editSeq[stackLevel].count + 1);
114 printf("EditPtr = %s\n", editSeq[stackLevel].editPtr);
115#endif
116 edit = editSeq[stackLevel].editPtr;
117 while (*(editSeq[stackLevel].editPtr = edit)) {
118 count = 0;
119 while (isdigit(*edit))
120 count = count * 10 + (*edit++ - '0');
121 if (!count)
122 count = 1;
123 if (!*edit)
124 break;
125#if DEBUG
126 printf("count = %ld command = %c\n", count, *edit);
127#endif
128 doPush = 0;
129 /* this was commented out to avoid reseting the kill buffer before each command except yanks
130 new_kill = 1;
131 */
132 switch (*edit) {
133 case '(':
134 /* expression */
135 parenCount = 1;
136 ptr = edit + 1;
137 while (parenCount && *++edit) {
138 if (*edit == '(')
139 parenCount++;
140 else if (*edit == ')')
141 parenCount--;
142 }
143 if (*edit) {
144 editSeq[stackLevel].pending = 1;
145 editSeq[stackLevel].editPtr = edit + 1;
146 } else {
147 editSeq[stackLevel].pending = 1;
148 editSeq[stackLevel].editPtr = NULL;
149 }
150 if (stackLevel >= stackDepth &&
151 !(editSeq = realloc(editSeq, sizeof(*editSeq) * (stackDepth += 10)))) {
152 fprintf(stderr, "memory allocation failure (edit_string)");
153 return 0;
154 }
155 stackLevel++;
156 editSeq[stackLevel].count = count;
157 cSave = *edit;
158 *edit = 0;
159 cp_str(&editSeq[stackLevel].editText, ptr);
160 *edit = cSave;
161 editSeq[stackLevel].editPtr = NULL;
162 doPush = 1;
163#if DEBUG
164 printf("stack pushing: %ld*>%s<\n", count,
165 editSeq[stackLevel].editText);
166#endif
167 break;
168 case 'c':
169 /* clear the kill buffer */
170 kill[0] = 0;
171 break;
172 case 'd':
173 /* delete N characters */
174 if (count > (long)strlen(text))
175 count = strlen(text);
176 strcpy_ss(text, text + count);
177 new_kill = 1;
178 break;
179 case 'f':
180 /* move forward N chars */
181 if (count > (long)strlen(text))
182 count = strlen(text);
183 text += count;
184 new_kill = 1;
185 break;
186 case 'b':
187 /* move backward N chars */
188 if ((text -= count) < text_start)
189 text = text_start;
190 new_kill = 1;
191 break;
192 case 'D':
193 /* delete N words */
194 while (count-- && (ptr = strpbrk(text, " \t_-"))) {
195 while (IS_WORD_END(*ptr))
196 ptr++;
197 strcpy_ss(text, ptr);
198 }
199 if (count >= 0)
200 *text = 0;
201 new_kill = 1;
202 break;
203 case 'F':
204 /* move forward N words */
205 while (count-- && (ptr = strpbrk(text, " \t_-"))) {
206 while (IS_WORD_END(*ptr))
207 ptr++;
208 text = ptr;
209 }
210 if (count >= 0)
211 text += strlen(text);
212 new_kill = 1;
213 break;
214 case 'B':
215 /* move backward N words */
216 while (count--) {
217 while (IS_WORD_END(*text) && text > text_start)
218 text--;
219 while (!IS_WORD_END(*text) && text > text_start)
220 text--;
221 }
222 if (IS_WORD_END(*text))
223 text++;
224 new_kill = 1;
225 break;
226 case 'a':
227 /* go to start of line */
228 text = text_start;
229 break;
230 case 'e':
231 /* go to end of line */
232 text += strlen(text);
233 break;
234 case 'i':
235 /* insert characters */
236 delimiter = *++edit;
237 ptr1 = NULL;
238 if ((ptr = strchr(++edit, delimiter))) {
239 ptr1 = ptr;
240 *ptr = 0;
241 } else {
242 ptr = edit + strlen(edit) - 1;
243 }
244#if DEBUG
245 printf("insert string = >%s<\n", edit);
246#endif
247 while (count--) {
248 insert(text, edit);
249 text += strlen(edit);
250 }
251 if (ptr1)
252 *ptr1 = delimiter;
253 edit = ptr;
254 new_kill = 1;
255 break;
256 case 'x':
257 /* kill anything in the given string */
258 delimiter = *++edit;
259 if (!*edit)
260 break;
261 invert = 0;
262 if (delimiter == '-') {
263 invert = 1;
264 delimiter = *++edit;
265 if (!*edit)
266 break;
267 }
268 delimLoc = NULL;
269 if ((editNext = strchr(++edit, delimiter))) {
270 delimLoc = editNext;
271 *editNext = 0;
272 } else {
273 editNext = edit + strlen(edit) - 1;
274 }
275 charList = expand_ranges(edit);
276 if (*charList == '[' && *(charList + strlen(charList) - 1) == ']') {
277 /*
278 strcpy_ss(charList, charList+1);
279 *(charList+strlen(charList)-1) = 0;
280*/
281 }
282#if DEBUG
283 printf("x-kill string = >%s< (was >%s<)\n", charList, edit);
284 printf("text position: >%s<\n", text);
285#endif
286 if (new_kill)
287 kill[0] = 0;
288 new_kill = 0;
289 if (invert) {
290 /* kill anything up to a character in the string */
291 if ((ptr = strpbrk(text, charList))) {
292 strncat(kill, text, ptr - text);
293 strcpy_ss(text, ptr);
294 } else
295 /* no occurrence, kill whole string */
296 *text = 0;
297 } else {
298 /* kill anything in the given string */
299 int i, length, found;
300 found = 1;
301 length = strlen(charList);
302 ptr = text;
303 while (found && *ptr) {
304 found = 0;
305 for (i = 0; i < length; i++) {
306 if (*ptr == *(charList + i)) {
307 found = 1;
308 ptr++;
309 break;
310 }
311 }
312 }
313 if (ptr != text) {
314 strncat(kill, text, ptr - text);
315 strcpy_ss(text, ptr);
316 }
317 }
318 free(charList);
319 if (delimLoc)
320 *delimLoc = delimiter;
321 edit = editNext;
322 break;
323 case 'r':
324 /* search backwards for characters, leaving cursor at end of matching section */
325 if (!(delimiter = *++edit))
326 return (0);
327 conditional = 0;
328 if (delimiter == '?') {
329 conditional = 1;
330 if (!(delimiter = *++edit))
331 return (0);
332 }
333 ptr1 = NULL;
334 if ((ptr = strchr(++edit, delimiter))) {
335 ptr1 = ptr;
336 *ptr = 0;
337 } else {
338 ptr = edit + strlen(edit) - 1;
339 }
340#if DEBUG
341 printf("search string = >%s<\n", edit);
342#endif
343 conditionalReturn = 0;
344 while (count--) {
345 j = strlen(text);
346 ptr3 = text;
347 text = text_start;
348 ptr2 = strstr(text, edit);
349 if (ptr2 == NULL) {
350 text = ptr3;
351 conditionalReturn = conditional;
352 break;
353 }
354 if (strlen(ptr2) <= j) {
355 text = ptr3;
356 conditionalReturn = conditional;
357 break;
358 }
359 text = ptr2 + strlen(edit);
360 while (1) {
361 ptr2 = strstr(text, edit);
362 if ((ptr2 == NULL) || (strlen(ptr2) <= j)) {
363 break;
364 }
365 text = ptr2 + strlen(edit);
366 }
367 if (count != 0)
368 text -= strlen(edit);
369 }
370 if (ptr1)
371 *ptr1 = delimiter;
372 edit = ptr;
373 new_kill = 1;
374 if (conditionalReturn)
375 return 1;
376 break;
377 case 'R':
378 /* search backwards for characters, but leave cursor at start of matching section */
379 if (!(delimiter = *++edit))
380 return (0);
381 conditional = 0;
382 if (delimiter == '?') {
383 conditional = 1;
384 if (!(delimiter = *++edit))
385 return (0);
386 }
387 ptr1 = NULL;
388 if ((ptr = strchr(++edit, delimiter))) {
389 ptr1 = ptr;
390 *ptr = 0;
391 } else {
392 ptr = edit + strlen(edit) - 1;
393 }
394#if DEBUG
395 printf("search string = >%s<\n", edit);
396#endif
397 conditionalReturn = 0;
398 while (count--) {
399 j = strlen(text);
400 ptr3 = text;
401 text = text_start;
402 ptr2 = strstr(text, edit);
403 if (ptr2 == NULL) {
404 text = ptr3;
405 conditionalReturn = conditional;
406 break;
407 }
408 if (strlen(ptr2) <= j) {
409 text = ptr3;
410 conditionalReturn = conditional;
411 break;
412 }
413 text = ptr2 + strlen(edit);
414 while (1) {
415 ptr2 = strstr(text, edit);
416 if ((ptr2 == NULL) || (strlen(ptr2) <= j)) {
417 break;
418 }
419 text = ptr2 + strlen(edit);
420 }
421 text -= strlen(edit);
422 }
423 if (ptr1)
424 *ptr1 = delimiter;
425 edit = ptr;
426 new_kill = 1;
427 if (conditionalReturn)
428 return 1;
429 break;
430 case 's':
431 /* search forward for characters, leaving CP at end of matching section */
432 if (!(delimiter = *++edit))
433 return (0);
434 conditional = 0;
435 if (delimiter == '?') {
436 conditional = 1;
437 if (!(delimiter = *++edit))
438 return (0);
439 }
440 ptr1 = NULL;
441 if ((ptr = strchr(++edit, delimiter))) {
442 ptr1 = ptr;
443 *ptr = 0;
444 } else {
445 ptr = edit + strlen(edit) - 1;
446 }
447#if DEBUG
448 printf("search string = >%s<\n", edit);
449#endif
450 conditionalReturn = 0;
451 while (count--) {
452 if ((ptr2 = strstr(text, edit)))
453 text = ptr2 + strlen(edit);
454 else {
455 conditionalReturn = conditional;
456 break;
457 }
458 }
459 if (ptr1)
460 *ptr1 = delimiter;
461 edit = ptr;
462 new_kill = 1;
463 if (conditionalReturn)
464 return 1;
465 break;
466 case 'S':
467 /* search forward for characters, but leave cursor at start of matching section */
468 if (!(delimiter = *++edit))
469 return (0);
470 conditional = 0;
471 if (delimiter == '?') {
472 conditional = 1;
473 if (!(delimiter = *++edit))
474 return (0);
475 }
476 ptr1 = NULL;
477 if ((ptr = strchr(++edit, delimiter))) {
478 ptr1 = ptr;
479 *ptr = 0;
480 } else {
481 ptr = edit + strlen(edit) - 1;
482 }
483#if DEBUG
484 printf("search string = >%s<\n", edit);
485#endif
486 conditionalReturn = 0;
487 while (count--) {
488 if ((ptr2 = strstr(text, edit))) {
489 if (count != 0)
490 text = ptr2 + strlen(edit);
491 else
492 text = ptr2;
493 } else {
494 conditionalReturn = conditional;
495 break;
496 }
497 }
498 if (ptr1)
499 *ptr1 = delimiter;
500 edit = ptr;
501 new_kill = 1;
502 if (conditionalReturn)
503 return 1;
504 break;
505 case 'k':
506 /* kill N characters */
507 if (new_kill)
508 kill[0] = 0;
509 strncat(kill, text, count);
510 strcpy_ss(text, text + count);
511 new_kill = 0;
512#if DEBUG
513 printf("kill buffer: >%s<\n", kill);
514#endif
515 break;
516 case 'K':
517 /* kill N words */
518 if (new_kill)
519 kill[0] = 0;
520 while (count-- && (ptr = strpbrk(text, " \t_-"))) {
521 while (IS_WORD_END(*ptr))
522 ptr++;
523 strncat(kill, text, ptr - text);
524 strcpy_ss(text, ptr);
525 }
526 if (count >= 0)
527 *text = 0;
528 new_kill = 0;
529#if DEBUG
530 printf("kill buffer: >%s<\n", kill);
531#endif
532 break;
533 case 'z':
534 case 'Z':
535 if (!(delimiter = *++edit))
536 return (0);
537 while (count--) {
538 /* kill up to next character */
539 if (new_kill)
540 kill[0] = 0;
541 if ((ptr = strchr(text, delimiter))) {
542 if (*(edit - 1) == 'Z')
543 ptr++; /* delete delimiter */
544 strncat(kill, text, ptr - text);
545 strcpy_ss(text, ptr);
546 }
547 new_kill = 0;
548#if DEBUG
549 printf("kill buffer: >%s<\n", kill);
550#endif
551 if (*(edit - 1) == 'z')
552 break;
553 }
554 break;
555 case 'y':
556 /* yank kill buffer */
557#if DEBUG
558 printf("yank string = >%s<\n", kill);
559#endif
560 while (count--) {
561 insert(text, kill);
562 text += strlen(kill);
563 }
564 break;
565 case '%':
566 /* replace one string with another */
567 global = here = 0;
568 if (*(edit + 1) == 'g') {
569 global = 1;
570 edit++;
571 }
572 if (*(edit + 1) == 'h') {
573 here = 1;
574 edit++;
575 }
576 if (!(delimiter = *++edit))
577 return (0);
578#if DEBUG
579 printf("delimiter = %c\n", delimiter);
580#endif
581 orig = edit + 1;
582 if (!(repl = strchr(orig + 1, delimiter)))
583 return (0);
584 *repl++ = 0;
585 if (!(ptr = strchr(repl, delimiter)))
586 return (0);
587 *ptr = 0;
588#if DEBUG
589 printf("orig: >%s< repl: >%s<\n", orig, repl);
590#endif
591 if (here) {
592 if (!global)
593 replaceString(buffer, text, orig, repl, count, 1);
594 else
595 replaceString(buffer, text, orig, repl, -1, 1);
596 } else {
597 if (!global)
598 replace_stringn(buffer, text, orig, repl, count);
599 else
600 replace_string(buffer, text, orig, repl);
601 }
602 strcpy_ss(text, buffer);
603 edit = ptr;
604 *(repl - 1) = delimiter;
605 *ptr = delimiter;
606 break;
607 default:
608 break;
609 }
610 if (doPush)
611 break;
612 edit++;
613 }
614 }
615 free(editSeq[stackLevel].editText);
616 editSeq[stackLevel].editPtr = NULL;
617 stackLevel--;
618 }
619 free(editSeq);
620 return 1;
621}
622
623/**
624 * @brief Applies edit commands to an array of strings.
625 *
626 * Iterates over an array of strings, applying the specified edit commands to each string.
627 * Utilizes a buffer for intermediate processing and ensures that each string is properly
628 * updated after editing. Frees the original string memory and replaces it with the edited version.
629 *
630 * @param string Double pointer to the array of strings to be edited.
631 * @param strings The number of strings in the array.
632 * @param buffer Pointer to a buffer used for temporary storage during editing.
633 * @param edit Pointer to the string containing edit commands to apply to each string.
634 */
635void edit_strings(char **string, long strings, char *buffer, char *edit) {
636 while (strings--) {
637 strcpy_ss(buffer, string[strings]);
638 edit_string(buffer, edit);
639 free(string[strings]);
640 cp_str(string + strings, buffer);
641 }
642}
char * cp_str(char **s, char *t)
Copies a string, allocating memory for storage.
Definition cp_str.c:28
void edit_strings(char **string, long strings, char *buffer, char *edit)
Applies edit commands to an array of strings.
long edit_string(char *text, char *edit)
Edits the provided text based on the specified edit commands.
Definition edit_string.c:75
char * insert(char *s, char *t)
Inserts a substring into a target string.
Definition insert.c:29
int replaceString(char *t, char *s, char *orig, char *repl, long count_limit, long here)
Replace occurrences of one string with another string with additional options.
int replace_stringn(char *t, char *s, char *orig, char *repl, long count_limit)
Replace a limited number of occurrences of one string with another string.
int replace_string(char *t, char *s, char *orig, char *repl)
Replace all occurrences of one string with another string.
char * strcpy_ss(char *dest, const char *src)
Safely copies a string, handling memory overlap.
Definition str_copy.c:34
char * expand_ranges(char *template)
Expand range specifiers in a wildcard template into explicit character lists.
Definition wild_match.c:429