1 /*
2 *
3 * Copyright (C) 2016-2018 by C.H. Huang
4 * plushuang.tw@gmail.com
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * ---
21 *
22 * In addition, as a special exception, the copyright holders give
23 * permission to link the code of portions of this program with the
24 * OpenSSL library under certain conditions as described in each
25 * individual source file, and distribute linked combinations
26 * including the two.
27 * You must obey the GNU Lesser General Public License in all respects
28 * for all of the code used other than OpenSSL. If you modify
29 * file(s) with this exception, you may extend this exception to your
30 * version of the file(s), but you are not obligated to do so. If you
31 * do not wish to do so, delete this exception statement from your
32 * version. If you delete this exception statement from all source
33 * files in the program, then also delete it here.
34 *
35 */
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <UgUtil.h>
40 #include <UgetSequence.h>
41
42 typedef struct UgLinkString UgLinkString;
43
44 struct UgLinkString
45 {
46 UG_LINK_MEMBERS (UgLinkString, char, data);
47 // char* data;
48 // UgLinkString* next;
49 // UgLinkString* prev;
50
51 char string[1];
52 };
53
ug_link_string_new(const char * string,int length)54 static UgLink* ug_link_string_new (const char* string, int length)
55 {
56 UgLinkString* link;
57
58 link = ug_malloc (sizeof (UgLinkString) + length);
59 link->data = link->string;
60 strcpy (link->string, string);
61 return (UgLink*) link;
62 }
63
64 // ----------------------------------------------------------------------------
65 // UgetSeqRange
uget_seq_range_to_first(UgetSeqRange * range)66 static void uget_seq_range_to_first (UgetSeqRange* range)
67 {
68 range->cur = range->first;
69 }
70
uget_seq_range_to_last(UgetSeqRange * range)71 static void uget_seq_range_to_last (UgetSeqRange* range)
72 {
73 range->cur = range->last;
74 }
75
76 // ----------------------------------------------------------------------------
77
uget_sequence_init(UgetSequence * useq)78 void uget_sequence_init (UgetSequence* useq)
79 {
80 ug_array_init (useq, sizeof (UgetSeqRange), 8);
81 ug_buffer_init (&useq->buf, 128);
82 }
83
uget_sequence_final(UgetSequence * useq)84 void uget_sequence_final (UgetSequence* useq)
85 {
86 ug_array_clear (useq);
87 ug_buffer_clear (&useq->buf, TRUE);
88 }
89
uget_sequence_add(UgetSequence * useq,uint32_t first,uint32_t last,int digits)90 void uget_sequence_add (UgetSequence* useq, uint32_t first, uint32_t last, int digits)
91 {
92 UgetSeqRange* range;
93
94 range = ug_array_alloc (useq, 1);
95 range->digits = digits;
96
97 if (first < last) {
98 range->first = first;
99 range->last = last;
100 }
101 else {
102 range->first = last;
103 range->last = first;
104 }
105 range->cur = range->first;
106 }
107
uget_sequence_clear(UgetSequence * useq)108 void uget_sequence_clear (UgetSequence* useq)
109 {
110 useq->length = 0;
111 }
112
uget_sequence_count(UgetSequence * useq,const char * pattern)113 int uget_sequence_count (UgetSequence* useq, const char* pattern)
114 {
115 UgetSeqRange* range;
116 UgetSeqRange* range_end;
117 const char* pcur;
118 int pcur_len;
119 int count;
120
121 count = 0;
122 range = useq->at;
123 range_end = useq->at + useq->length;
124
125 for (pcur = pattern; pcur[0] && range < range_end; pcur++, range++) {
126 pcur_len = strcspn (pcur, "*");
127 if (pcur[pcur_len] != '*')
128 break;
129
130 if (count == 0)
131 count = range->last - range->first + 1;
132 else
133 count = count * (range->last - range->first + 1);
134
135 pcur += pcur_len; // to next '*'
136 }
137
138 return count;
139 }
140
141 // generate string by pattern
uget_sequence_generate1(UgetSequence * useq,const char * pattern)142 static char* uget_sequence_generate1 (UgetSequence* useq, const char* pattern)
143 {
144 UgetSeqRange* range;
145 char* utf8;
146 int length;
147 const char* pcur;
148 int pcur_len;
149
150 range = useq->at;
151 useq->buf.cur = useq->buf.beg;
152
153 for (pcur = pattern; pcur[0]; pcur++) {
154 pcur_len = strcspn (pcur, "*");
155 ug_buffer_write (&useq->buf, pcur, pcur_len);
156 if (pcur[pcur_len] != '*')
157 break;
158
159 if (range->digits == 0) {
160 // ASCII or Unicode
161 if (range->cur < 0x80)
162 ug_buffer_write_char (&useq->buf, range->cur);
163 else {
164 utf8 = ug_ucs4_to_utf8 (&range->cur, 1, &length);
165 ug_buffer_write (&useq->buf, utf8, length);
166 ug_free (utf8);
167 }
168 }
169 else {
170 // digits, 0 - 9
171 #ifdef _MSC_VER // for MS C only
172 length = _scprintf ("%.*u", range->digits, range->cur);
173 #else // for C99 standard
174 length = snprintf (NULL, 0, "%.*u", range->digits, range->cur);
175 #endif
176 sprintf (ug_buffer_alloc (&useq->buf, length + 1),
177 "%.*u", range->digits, range->cur);
178 useq->buf.cur--; // remove null character in tail
179 }
180
181 if (++range > useq->range_last)
182 range = useq->at;
183 pcur += pcur_len; // to next '*'
184 }
185
186 ug_buffer_write_char (&useq->buf, 0);
187 return useq->buf.beg;
188 }
189
uget_sequence_generate(UgetSequence * useq,const char * pattern,UgetSeqRange * range,UgList * result)190 static int uget_sequence_generate (UgetSequence* useq, const char* pattern, UgetSeqRange* range, UgList* result)
191 {
192 UgLink* link;
193 int count;
194
195 for (count = 0; range->cur <= range->last; range->cur++, count++) {
196 if (range+1 <= useq->range_last)
197 count += uget_sequence_generate (useq, pattern, range+1, result);
198 else {
199 uget_sequence_generate1 (useq, pattern);
200 link = ug_link_string_new (useq->buf.beg, ug_buffer_length (&useq->buf));
201 ug_list_append (result, link);
202 }
203 }
204
205 range->cur = range->first;
206 return count;
207 }
208
uget_sequence_decide_range_last(UgetSequence * useq,const char * pattern)209 static UgetSeqRange* uget_sequence_decide_range_last (UgetSequence* useq, const char* pattern)
210 {
211 const char* wildcard;
212 int count;
213
214 if (useq->length == 0)
215 return NULL;
216
217 // count wildcard character (*) to decide the last UgetSeqRange
218 for (count = 0, wildcard = pattern; wildcard[0]; wildcard++) {
219 if (wildcard[0] == '*')
220 count++;
221 }
222 if (count < useq->length)
223 useq->range_last = useq->at + count -1;
224 else
225 useq->range_last = useq->at + useq->length -1;
226
227 return useq->range_last;
228 }
229
uget_sequence_get_list(UgetSequence * useq,const char * pattern,UgList * result)230 int uget_sequence_get_list (UgetSequence* useq, const char* pattern, UgList* result)
231 {
232 if (uget_sequence_decide_range_last(useq, pattern) == NULL)
233 return 0;
234
235 // reset range
236 ug_array_foreach (useq, (UgForeachFunc)uget_seq_range_to_first, NULL);
237 // generate list
238 return uget_sequence_generate (useq, pattern, useq->at, result);
239 }
240
uget_sequence_get_preview(UgetSequence * useq,const char * pattern,UgList * result)241 int uget_sequence_get_preview (UgetSequence* useq, const char* pattern, UgList* result)
242 {
243 UgetSeqRange* range_last;
244 UgetSeqRange* range_prev;
245 UgLink* link;
246 int count;
247
248 // use uget_sequence_get_list() to generate preview if possible.
249 count = uget_sequence_count (useq, pattern);
250 if (count < 6)
251 return uget_sequence_get_list (useq, pattern, result);
252
253 range_last = uget_sequence_decide_range_last(useq, pattern);
254 // decide previous UgetSeqRange by the last UgetSeqRange
255 for (range_prev = range_last; range_prev != useq->at; range_prev--) {
256 if (range_prev->last - range_prev->first + 1 > 1)
257 break;
258 }
259
260 // reset range to first
261 ug_array_foreach (useq, (UgForeachFunc)uget_seq_range_to_first, NULL);
262
263 // create 1st string
264 uget_sequence_generate1 (useq, pattern);
265 link = ug_link_string_new (useq->buf.beg, ug_buffer_length (&useq->buf));
266 ug_list_append (result, link);
267 // create 2nd string
268 range_prev->cur++;
269 uget_sequence_generate1 (useq, pattern);
270 link = ug_link_string_new (useq->buf.beg, ug_buffer_length (&useq->buf));
271 ug_list_append (result, link);
272
273 // create 3rd " ..."
274 link = ug_link_string_new (" ...", 4);
275 ug_list_append (result, link);
276
277 // reset range to last
278 ug_array_foreach (useq, (UgForeachFunc)uget_seq_range_to_last, NULL);
279
280 // create 4th string
281 range_prev->cur = range_prev->last - 1;
282 uget_sequence_generate1 (useq, pattern);
283 link = ug_link_string_new (useq->buf.beg, ug_buffer_length (&useq->buf));
284 ug_list_append (result, link);
285 // create 5th string
286 range_prev->cur = range_prev->last;
287 uget_sequence_generate1 (useq, pattern);
288 link = ug_link_string_new (useq->buf.beg, ug_buffer_length (&useq->buf));
289 ug_list_append (result, link);
290
291 return 5;
292 }
293
294