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