let all function types return NO_TAG
[nedit-bw.git] / macroSplit.diff
blob27437c62c016ee86425c8cf140310ac3ffcb1a1a
1 Extending the split() macro built-in
3 This patch extends split()'s functionality. It allows limited splitting,
4 where only a certain number of elements should be retrieved, and also
5 allows the dropping of the last element found if it is empty.
7 The limited count avoids the perhaps unnecessary overhead of generating a
8 large array if only the first few elements are to be used. The dropping of
9 the empty last element allows simple reconstruction after simple splits,
10 very useful for lines. For example, given:
12 a = get_range(0, $text_length)
13 lines = split(a, "\n", "lastnotnull")
14 b = ""
15 for (i = 0; i < lines[]; i++)
16 b = b lines[i] "\n"
18 assuming that all lines are '\n' terminated, b == a at the end. Otherwise
19 we have to resort to something like:
21 a = get_range(0, $text_length)
22 lines = split(a, "\n")
23 b = ""
24 sep = ""
25 for (i = 0; i < lines[]; i++) {
26 b = b sep lines[i]
27 sep = "\n"
30 to make a == b at the end. which is trickier (albeit more general).
32 ---
34 source/macro.c | 102 ++++++++++++++++++++++++++++++++++++++++++++-------------
35 1 files changed, 80 insertions(+), 22 deletions(-)
37 diff --quilt old/source/macro.c new/source/macro.c
38 --- old/source/macro.c
39 +++ new/source/macro.c
40 @@ -3960,18 +3960,43 @@ static int stringCompareMS(WindowInfo *w
41 result->val.n = compareResult;
42 return True;
46 -** This function is intended to split strings into an array of substrings
47 -** Importatnt note: It should always return at least one entry with key 0
48 -** split("", ",") result[0] = ""
49 -** split("1,2", ",") result[0] = "1" result[1] = "2"
50 -** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
51 -**
52 -** This behavior is specifically important when used to break up
53 -** array sub-scripts
54 +** This function is intended to split strings into an array of substrings.
55 +**
56 +** array = split(string, separator[, searchType][, count][, "lastnotnull"])
57 +**
58 +** Mandatory arguments:
59 +** string: string to split,
60 +** string: separator string or pattern marking where to split
61 +** Optional arguments:
62 +** searchType: separator search type (default is "literal") to use to find
63 +** occurrences of separator in string.
64 +** count: maximum number of pieces in the returned array (default is
65 +** infinite, must be greater than zero); if smaller than or equal to
66 +** the number of separators found in string, the last piece will
67 +** contain the remainder of the string to split (a count of 1 produces
68 +** a single result in the returned array, equal to the original
69 +** string).
70 +** keyword "lastnotnull": if present, this stops an empty string being
71 +** returned in the last entry of the array if the string to split ends
72 +** with the separator. This has the effect of returning an empty array
73 +** if the string to split is originally empty. Otherwise, the returned
74 +** array will always contain at least one element.
75 +**
76 +** Important note: It should always return at least one entry with key 0
77 +** unless "lastnotnull" is present.
78 +**
79 +** eg
80 +** split("", ",") result[0] = ""
81 +** split(",", ",") result[0] = "" result[1] = ""
82 +** split("1,2", ",") result[0] = "1" result[1] = "2"
83 +** split("1,2,", ",") result[0] = "1" result[1] = "2" result[2] = ""
84 +**
85 +** This behavior is specifically important when used to break up
86 +** array sub-scripts (unless "lastnotnull" is present)
89 static int splitMS(WindowInfo *window, DataValue *argList, int nArgs,
90 DataValue *result, char **errMsg)
92 @@ -3980,12 +4005,17 @@ static int splitMS(WindowInfo *window, D
93 int searchType, beginPos, foundStart, foundEnd, strLength, lastEnd;
94 int found, elementEnd, indexNum;
95 char indexStr[TYPE_INT_STR_SIZE(int)], *allocIndexStr;
96 DataValue element;
97 int elementLen;
99 - if (nArgs < 2) {
100 + int haveSearchType = False;
101 + int haveCount = False;
102 + int count = 0;
103 + int lastnotnull = False;
104 + int haveLastnotnull = False;
106 + if (nArgs < 2 || nArgs > 4) {
107 return(wrongNArgsErr(errMsg));
109 if (!readStringArg(argList[0], &sourceStr, stringStorage[0], errMsg)) {
110 *errMsg = "first argument must be a string: %s";
111 return(False);
112 @@ -4000,20 +4030,44 @@ static int splitMS(WindowInfo *window, D
114 if (splitStr == NULL) {
115 *errMsg = "second argument must be a non-empty string: %s";
116 return(False);
118 - if (nArgs > 2 && readStringArg(argList[2], &typeSplitStr, stringStorage[2], errMsg)) {
119 - if (!StringToSearchType(typeSplitStr, &searchType)) {
121 + /* get the search type and maximum element count */
122 + searchType = SEARCH_LITERAL;
123 + for (indexNum = 2; indexNum < nArgs; indexNum++) {
124 + if (!readStringArg(argList[indexNum], &typeSplitStr,
125 + stringStorage[indexNum], errMsg)) {
126 + *errMsg = "non-scalar arguments not allowed: %s";
127 + return(False);
129 + if (strcmp(typeSplitStr, "lastnotnull") == 0) {
130 + lastnotnull = True;
131 + if (haveLastnotnull) {
132 + *errMsg = "\"lastnotnull\" specified more than once: %s";
133 + return(False);
135 + } else if (StringToSearchType(typeSplitStr, &searchType)) {
136 + if (haveSearchType) {
137 + *errMsg = "split search type supplied more than once: %s";
138 + return(False);
140 + haveSearchType = True;
141 + } else if (!haveCount &&
142 + readIntArg(argList[indexNum], &count, errMsg)) {
143 + haveCount = True;
144 + if (count < 1) {
145 + *errMsg = "split maximum count must be greater than 0: %s";
146 + return(False);
148 + } else {
149 *errMsg = "unrecognized argument to %s";
150 return(False);
153 - else {
154 - searchType = SEARCH_LITERAL;
158 result->tag = ARRAY_TAG;
159 result->val.arrayPtr = ArrayNew();
161 beginPos = 0;
162 lastEnd = 0;
163 @@ -4026,13 +4080,17 @@ static int splitMS(WindowInfo *window, D
164 if (!allocIndexStr) {
165 *errMsg = "array element failed to allocate key: %s";
166 return(False);
168 strcpy(allocIndexStr, indexStr);
169 - found = SearchString(sourceStr, splitStr, SEARCH_FORWARD, searchType,
170 - False, beginPos, &foundStart, &foundEnd,
171 - NULL, NULL, GetWindowDelimiters(window));
172 + if (haveCount && --count == 0) {
173 + found = 0;
174 + } else {
175 + found = SearchString(sourceStr, splitStr, SEARCH_FORWARD,
176 + searchType, False, beginPos, &foundStart, &foundEnd,
177 + NULL, NULL, GetWindowDelimiters(window));
179 elementEnd = found ? foundStart : strLength;
180 elementLen = elementEnd - lastEnd;
181 element.tag = STRING_TAG;
182 if (!AllocNStringNCpy(&element.val.str, &sourceStr[lastEnd], elementLen)) {
183 *errMsg = "failed to allocate element value: %s";
184 @@ -4062,12 +4120,12 @@ static int splitMS(WindowInfo *window, D
185 *errMsg = "array element failed to allocate key: %s";
186 return(False);
188 strcpy(allocIndexStr, indexStr);
189 element.tag = STRING_TAG;
190 - if (lastEnd == strLength) {
191 - /* The pattern mathed the end of the string. Add an empty chunk. */
192 + if (lastEnd == strLength && !lastnotnull) {
193 + /* The pattern matched the end of the string. Add an empty chunk. */
194 element.val.str.rep = PERM_ALLOC_STR("");
195 element.val.str.len = 0;
197 if (!ArrayInsert(result, allocIndexStr, &element)) {
198 M_ARRAY_INSERT_FAILURE();
199 @@ -4094,11 +4152,11 @@ static int splitMS(WindowInfo *window, D
200 verify whether the pattern also matches the end of the string,
201 and add an empty chunk in case it does. */
202 found = SearchString(sourceStr, splitStr, SEARCH_FORWARD,
203 searchType, False, strLength, &foundStart, &foundEnd,
204 NULL, NULL, GetWindowDelimiters(window));
205 - if (found) {
206 + if (found && !lastnotnull) {
207 ++indexNum;
208 sprintf(indexStr, "%d", indexNum);
209 allocIndexStr = AllocString(strlen(indexStr) + 1);
210 if (!allocIndexStr) {
211 *errMsg = "array element failed to allocate key: %s";