1 | /* ============================================================= */ |
---|
2 | /* */ |
---|
3 | /* File : adstring.c */ |
---|
4 | /* Purpose : various string functions */ |
---|
5 | /* */ |
---|
6 | /* Institute of Microbiology (Technical University Munich) */ |
---|
7 | /* http://www.arb-home.de/ */ |
---|
8 | /* */ |
---|
9 | /* ============================================================= */ |
---|
10 | #include "adlocal.h" |
---|
11 | |
---|
12 | #include <stdlib.h> |
---|
13 | #include <string.h> |
---|
14 | #include <ctype.h> |
---|
15 | #include <errno.h> |
---|
16 | #include <stdarg.h> |
---|
17 | #include <dirent.h> |
---|
18 | #include <sys/stat.h> |
---|
19 | #include <execinfo.h> |
---|
20 | #include <signal.h> |
---|
21 | |
---|
22 | |
---|
23 | |
---|
24 | /******************************************************************************************** |
---|
25 | directory handling |
---|
26 | ********************************************************************************************/ |
---|
27 | |
---|
28 | GB_ERROR gb_scan_directory(char *basename, struct gb_scandir *sd) { /* goes to header: __ATTR__USERESULT */ |
---|
29 | /* look for quick saves (basename = yyy/xxx no arb ending !!!!) */ |
---|
30 | char *path = strdup(basename); |
---|
31 | const char *fulldir = "."; |
---|
32 | char *file = strrchr(path,'/'); |
---|
33 | DIR *dirp; |
---|
34 | int curindex; |
---|
35 | char *suffix; |
---|
36 | struct dirent *dp; |
---|
37 | struct stat st; |
---|
38 | const char *oldstyle = ".arb.quick"; |
---|
39 | char buffer[GB_PATH_MAX]; |
---|
40 | int oldstylelen = strlen(oldstyle); |
---|
41 | int filelen; |
---|
42 | |
---|
43 | if (file) { |
---|
44 | *(file++) = 0; |
---|
45 | fulldir = path; |
---|
46 | } else { |
---|
47 | file = path; |
---|
48 | } |
---|
49 | |
---|
50 | memset((char*)sd,0,sizeof(*sd)); |
---|
51 | sd->type = GB_SCAN_NO_QUICK; |
---|
52 | sd->highest_quick_index = -1; |
---|
53 | sd->newest_quick_index = -1; |
---|
54 | sd->date_of_quick_file = 0; |
---|
55 | |
---|
56 | dirp = opendir(fulldir); |
---|
57 | if (!dirp){ |
---|
58 | GB_ERROR error = GB_export_errorf("Directory %s of file %s.arb not readable",fulldir,file); |
---|
59 | free(path); |
---|
60 | return error; |
---|
61 | } |
---|
62 | filelen = strlen(file); |
---|
63 | for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){ |
---|
64 | if (strncmp(dp->d_name,file,filelen)) continue; |
---|
65 | suffix = dp->d_name + filelen; |
---|
66 | if (suffix[0] != '.') continue; |
---|
67 | if (!strncmp(suffix,oldstyle,oldstylelen)){ |
---|
68 | if (sd->type == GB_SCAN_NEW_QUICK){ |
---|
69 | printf("Warning: Found new and old changes files, using new\n"); |
---|
70 | continue; |
---|
71 | } |
---|
72 | sd->type = GB_SCAN_OLD_QUICK; |
---|
73 | curindex = atoi(suffix+oldstylelen); |
---|
74 | goto check_time_and_date; |
---|
75 | } |
---|
76 | if (strlen(suffix) == 4 && |
---|
77 | suffix[0] == '.' && |
---|
78 | suffix[1] == 'a' && |
---|
79 | isdigit(suffix[2]) && |
---|
80 | isdigit(suffix[3])){ |
---|
81 | if (sd->type == GB_SCAN_OLD_QUICK){ |
---|
82 | printf("Warning: Found new and old changes files, using new\n"); |
---|
83 | } |
---|
84 | sd->type = GB_SCAN_NEW_QUICK; |
---|
85 | curindex = atoi(suffix+2); |
---|
86 | goto check_time_and_date; |
---|
87 | } |
---|
88 | continue; |
---|
89 | check_time_and_date: |
---|
90 | if (curindex > sd->highest_quick_index) sd->highest_quick_index = curindex; |
---|
91 | sprintf(buffer,"%s/%s",fulldir,dp->d_name); |
---|
92 | stat(buffer,&st); |
---|
93 | if ((unsigned long)st.st_mtime > sd->date_of_quick_file){ |
---|
94 | sd->date_of_quick_file = st.st_mtime; |
---|
95 | sd->newest_quick_index = curindex; |
---|
96 | } |
---|
97 | continue; |
---|
98 | } |
---|
99 | closedir(dirp); |
---|
100 | free(path); |
---|
101 | return 0; |
---|
102 | } |
---|
103 | |
---|
104 | |
---|
105 | char *GB_find_all_files(const char *dir,const char *mask, GB_BOOL filename_only) { |
---|
106 | /* Returns a string containing the filenames of all files matching mask. |
---|
107 | The single filenames are seperated by '*'. |
---|
108 | if 'filename_only' is true -> string contains only filenames w/o path |
---|
109 | |
---|
110 | returns 0 if no files found (or directory not found). |
---|
111 | in this case an error may be exported |
---|
112 | |
---|
113 | 'mask' may contain wildcards (*?) or |
---|
114 | it may be a regular expression ('/regexp/') |
---|
115 | */ |
---|
116 | |
---|
117 | DIR *dirp; |
---|
118 | struct dirent *dp; |
---|
119 | struct stat st; |
---|
120 | char *result = 0; |
---|
121 | char buffer[GB_PATH_MAX]; |
---|
122 | |
---|
123 | dirp = opendir(dir); |
---|
124 | if (dirp) { |
---|
125 | GBS_MATCHER *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE); |
---|
126 | if (matcher) { |
---|
127 | for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { |
---|
128 | if (GBS_string_matches_regexp(dp->d_name, matcher)) { |
---|
129 | sprintf(buffer,"%s/%s",dir,dp->d_name); |
---|
130 | if (stat(buffer,&st) == 0 && S_ISREG(st.st_mode)) { // regular file ? |
---|
131 | if (filename_only) strcpy(buffer, dp->d_name); |
---|
132 | if (result) { |
---|
133 | freeset(result, GBS_global_string_copy("%s*%s", result, buffer)); |
---|
134 | } |
---|
135 | else { |
---|
136 | result = strdup(buffer); |
---|
137 | } |
---|
138 | } |
---|
139 | } |
---|
140 | } |
---|
141 | GBS_free_matcher(matcher); |
---|
142 | } |
---|
143 | closedir(dirp); |
---|
144 | } |
---|
145 | |
---|
146 | return result; |
---|
147 | } |
---|
148 | |
---|
149 | char *GB_find_latest_file(const char *dir, const char *mask) { |
---|
150 | /* returns the name of the newest file in dir 'dir' matching 'mask' |
---|
151 | * or NULL (in this case an error may be exported) |
---|
152 | * |
---|
153 | * 'mask' may contain wildcards (*?) or |
---|
154 | * it may be a regular expression ('/regexp/') |
---|
155 | */ |
---|
156 | |
---|
157 | DIR *dirp; |
---|
158 | struct dirent *dp; |
---|
159 | char buffer[GB_PATH_MAX]; |
---|
160 | struct stat st; |
---|
161 | GB_ULONG newest = 0; |
---|
162 | char *result = 0; |
---|
163 | |
---|
164 | dirp = opendir(dir); |
---|
165 | if (dirp) { |
---|
166 | GBS_MATCHER *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE); |
---|
167 | if (matcher) { |
---|
168 | for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { |
---|
169 | if (GBS_string_matches_regexp(dp->d_name, matcher)) { |
---|
170 | sprintf(buffer,"%s/%s",dir,dp->d_name); |
---|
171 | if (stat(buffer,&st) == 0) { |
---|
172 | if ((GB_ULONG)st.st_mtime > newest) { |
---|
173 | newest = st.st_mtime; |
---|
174 | freedup(result, dp->d_name); |
---|
175 | } |
---|
176 | } |
---|
177 | } |
---|
178 | } |
---|
179 | GBS_free_matcher(matcher); |
---|
180 | } |
---|
181 | closedir(dirp); |
---|
182 | } |
---|
183 | return result; |
---|
184 | } |
---|
185 | |
---|
186 | gb_warning_func_type gb_warning_func; |
---|
187 | gb_information_func_type gb_information_func; |
---|
188 | gb_status_func_type gb_status_func; |
---|
189 | gb_status_func2_type gb_status_func2; |
---|
190 | |
---|
191 | /******************************************************************************************** |
---|
192 | error handling |
---|
193 | ********************************************************************************************/ |
---|
194 | |
---|
195 | void GB_raise_critical_error(const char *msg) { |
---|
196 | fprintf(stderr, "------------------------------------------------------------\n"); |
---|
197 | fprintf(stderr, "A critical error occurred in ARB\nError-Message: %s\n", msg); |
---|
198 | #if defined(DEBUG) |
---|
199 | fprintf(stderr, "Run the debugger to find the location where the error was raised.\n"); |
---|
200 | #endif /* DEBUG */ |
---|
201 | fprintf(stderr, "------------------------------------------------------------\n"); |
---|
202 | gb_assert(0); |
---|
203 | exit(-1); |
---|
204 | } |
---|
205 | |
---|
206 | #if defined(DEVEL_RALF) |
---|
207 | #warning redesign GB_export_error et al |
---|
208 | /* To clearly distinguish between the two ways of error handling |
---|
209 | * (which are: return GB_ERROR |
---|
210 | * and: export the error) |
---|
211 | * |
---|
212 | * GB_export_error() shall only export, not return the error message. |
---|
213 | * if only used for formatting GBS_global_string shall be used |
---|
214 | * (most cases where GB_export_errorf is used are candidates for this. |
---|
215 | * GB_export_error was generally misused for this, before |
---|
216 | * GBS_global_string was added!) |
---|
217 | * |
---|
218 | * GB_export_IO_error() shall not export and be renamed into GB_IO_error() |
---|
219 | * |
---|
220 | * GB_export_error() shall fail if there is already an exported error |
---|
221 | * (maybe always remember a stack trace of last error export (try whether copy of backtrace-array works)) |
---|
222 | * |
---|
223 | * use GB_get_error() to import AND clear the error |
---|
224 | */ |
---|
225 | #endif /* DEVEL_RALF */ |
---|
226 | |
---|
227 | static char *GB_error_buffer = 0; |
---|
228 | |
---|
229 | GB_ERROR GB_export_error(const char *error) { // just a temp hack around format-warnings |
---|
230 | return GB_export_errorf("%s", error); |
---|
231 | } |
---|
232 | |
---|
233 | GB_ERROR GB_export_errorf(const char *templat, ...) { |
---|
234 | /* goes to header: __ATTR__FORMAT(1) __ATTR__DEPRECATED */ |
---|
235 | |
---|
236 | char buffer[GBS_GLOBAL_STRING_SIZE]; |
---|
237 | char *p = buffer; |
---|
238 | va_list parg; |
---|
239 | |
---|
240 | memset(buffer,0,1000); |
---|
241 | |
---|
242 | #if defined(DEVEL_RALF) |
---|
243 | #warning dont prepend error here |
---|
244 | #endif /* DEVEL_RALF */ |
---|
245 | |
---|
246 | p += sprintf(buffer,"ARB ERROR: "); |
---|
247 | va_start(parg,templat); |
---|
248 | |
---|
249 | vsprintf(p,templat,parg); |
---|
250 | |
---|
251 | freedup(GB_error_buffer, buffer); |
---|
252 | return GB_error_buffer; |
---|
253 | } |
---|
254 | |
---|
255 | GB_ERROR GB_export_IO_error(const char *action, const char *filename) { |
---|
256 | /* like GB_export_error() - creates error message from current 'errno' |
---|
257 | action may be NULL (otherwise it should contain sth like "writing" or "deleting") |
---|
258 | filename may be NULL (otherwise it should contain the filename, the IO-Error occurred for) |
---|
259 | */ |
---|
260 | |
---|
261 | const char *error_message; |
---|
262 | if (errno) { |
---|
263 | error_message = strerror(errno); |
---|
264 | } |
---|
265 | else { |
---|
266 | ad_assert(0); // unhandled error (which is NOT an IO-Error) |
---|
267 | error_message = |
---|
268 | "Some unhandled error occurred, but it was not an IO-Error. " |
---|
269 | "Please send detailed information about how the error occurred to devel@arb-home.de " |
---|
270 | "or ignore this error (if possible)."; |
---|
271 | } |
---|
272 | |
---|
273 | { |
---|
274 | char buffer[GBS_GLOBAL_STRING_SIZE]; |
---|
275 | |
---|
276 | if (action) { |
---|
277 | if (filename) sprintf(buffer, "ARB ERROR: While %s '%s': %s", action, filename, error_message); |
---|
278 | else sprintf(buffer, "ARB ERROR: While %s <unknown file>: %s", action, error_message); |
---|
279 | } |
---|
280 | else { |
---|
281 | if (filename) sprintf(buffer, "ARB ERROR: Concerning '%s': %s", filename, error_message); |
---|
282 | else sprintf(buffer, "ARB ERROR: %s", error_message); |
---|
283 | } |
---|
284 | |
---|
285 | freedup(GB_error_buffer, buffer); |
---|
286 | } |
---|
287 | return GB_error_buffer; |
---|
288 | } |
---|
289 | |
---|
290 | GB_ERROR GB_print_error() { |
---|
291 | if (GB_error_buffer){ |
---|
292 | fflush(stdout); |
---|
293 | fprintf(stderr,"%s\n",GB_error_buffer); |
---|
294 | } |
---|
295 | return GB_error_buffer; |
---|
296 | } |
---|
297 | |
---|
298 | GB_ERROR GB_get_error() { |
---|
299 | /* goes to header: __ATTR__DEPRECATED */ |
---|
300 | |
---|
301 | /* This function is deprecated. |
---|
302 | * Instead use either |
---|
303 | * - GB_have_error() or |
---|
304 | * - GB_await_error() |
---|
305 | */ |
---|
306 | |
---|
307 | return GB_error_buffer; |
---|
308 | } |
---|
309 | |
---|
310 | GB_BOOL GB_have_error() { |
---|
311 | return GB_error_buffer != 0; |
---|
312 | } |
---|
313 | |
---|
314 | GB_ERROR GB_await_error() { |
---|
315 | if (GB_error_buffer) { |
---|
316 | static char *err = 0; |
---|
317 | reassign(err, GB_error_buffer); |
---|
318 | return err; |
---|
319 | } |
---|
320 | ad_assert(0); // please correct error handling |
---|
321 | return "Program logic error: Something went wrong, but reason is unknown"; |
---|
322 | } |
---|
323 | |
---|
324 | void GB_clear_error() { /* clears the error buffer */ |
---|
325 | freeset(GB_error_buffer, 0); |
---|
326 | } |
---|
327 | |
---|
328 | #if defined(DEVEL_RALF) |
---|
329 | #warning search for 'GBS_global_string.*error' and replace with GB_failedTo_error |
---|
330 | #endif /* DEVEL_RALF */ |
---|
331 | GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error) { |
---|
332 | if (error) { |
---|
333 | if (special) { |
---|
334 | error = GBS_global_string("Failed to %s '%s'.\n(Reason: %s)", do_something, special, error); |
---|
335 | } |
---|
336 | else { |
---|
337 | error = GBS_global_string("Failed to %s.\n(Reason: %s)", do_something, error); |
---|
338 | } |
---|
339 | } |
---|
340 | return error; |
---|
341 | } |
---|
342 | |
---|
343 | /* -------------------------------------------------------------------------------- */ |
---|
344 | |
---|
345 | #ifdef LINUX |
---|
346 | # define HAVE_VSNPRINTF |
---|
347 | #endif |
---|
348 | |
---|
349 | #ifdef HAVE_VSNPRINTF |
---|
350 | # define PRINT2BUFFER(buffer, bufsize, templat, parg) vsnprintf(buffer, bufsize, templat, parg); |
---|
351 | #else |
---|
352 | # define PRINT2BUFFER(buffer, bufsize, templat, parg) vsprintf(buffer, templat, parg); |
---|
353 | #endif |
---|
354 | |
---|
355 | #define PRINT2BUFFER_CHECKED(printed, buffer, bufsize, templat, parg) \ |
---|
356 | (printed) = PRINT2BUFFER(buffer, bufsize, templat, parg); \ |
---|
357 | if ((printed) < 0 || (size_t)(printed) >= (bufsize)) { \ |
---|
358 | GBK_terminatef("Internal buffer overflow (size=%zu, used=%i)\n", \ |
---|
359 | (bufsize), (printed)); \ |
---|
360 | } |
---|
361 | |
---|
362 | /* -------------------------------------------------------------------------------- */ |
---|
363 | |
---|
364 | #if defined(DEBUG) |
---|
365 | #if defined(DEVEL_RALF) |
---|
366 | /* #define TRACE_BUFFER_USAGE */ |
---|
367 | #endif /* DEBUG */ |
---|
368 | #endif /* DEVEL_RALF */ |
---|
369 | |
---|
370 | #define GLOBAL_STRING_BUFFERS 4 |
---|
371 | |
---|
372 | static size_t last_global_string_size = 0; |
---|
373 | |
---|
374 | static GB_CSTR gbs_vglobal_string(const char *templat, va_list parg, int allow_reuse) |
---|
375 | { |
---|
376 | static char buffer[GLOBAL_STRING_BUFFERS][GBS_GLOBAL_STRING_SIZE+2]; // serveral buffers - used alternately |
---|
377 | static int idx = 0; |
---|
378 | static char lifetime[GLOBAL_STRING_BUFFERS] = { }; |
---|
379 | static char nextIdx[GLOBAL_STRING_BUFFERS] = { }; |
---|
380 | |
---|
381 | int my_idx; |
---|
382 | int psize; |
---|
383 | |
---|
384 | if (nextIdx[0] == 0) { // initialize nextIdx |
---|
385 | for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) { |
---|
386 | nextIdx[my_idx] = (my_idx+1)%GLOBAL_STRING_BUFFERS; |
---|
387 | } |
---|
388 | } |
---|
389 | |
---|
390 | if (allow_reuse == -1) { /* called from GBS_reuse_buffer */ |
---|
391 | /* buffer to reuse is passed in 'templat' */ |
---|
392 | |
---|
393 | for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) { |
---|
394 | if (buffer[my_idx] == templat) { |
---|
395 | lifetime[my_idx] = 0; |
---|
396 | #if defined(TRACE_BUFFER_USAGE) |
---|
397 | printf("Reusing buffer #%i\n", my_idx); |
---|
398 | #endif /* TRACE_BUFFER_USAGE */ |
---|
399 | if (nextIdx[my_idx] == idx) idx = my_idx; |
---|
400 | return 0; |
---|
401 | } |
---|
402 | #if defined(TRACE_BUFFER_USAGE) |
---|
403 | else { |
---|
404 | printf("(buffer to reuse is not buffer #%i (%p))\n", my_idx, buffer[my_idx]); |
---|
405 | } |
---|
406 | #endif /* TRACE_BUFFER_USAGE */ |
---|
407 | } |
---|
408 | for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) { |
---|
409 | printf("buffer[%i]=%p\n", my_idx, buffer[my_idx]); |
---|
410 | } |
---|
411 | ad_assert(0); /* GBS_reuse_buffer called with illegal buffer */ |
---|
412 | return 0; |
---|
413 | } |
---|
414 | |
---|
415 | if (lifetime[idx] == 0) { |
---|
416 | my_idx = idx; |
---|
417 | } |
---|
418 | else { |
---|
419 | for (my_idx = nextIdx[idx]; lifetime[my_idx]>0; my_idx = nextIdx[my_idx]) { |
---|
420 | #if defined(TRACE_BUFFER_USAGE) |
---|
421 | printf("decreasing lifetime[%i] (%i->%i)\n", my_idx, lifetime[my_idx], lifetime[my_idx]-1); |
---|
422 | #endif /* TRACE_BUFFER_USAGE */ |
---|
423 | lifetime[my_idx]--; |
---|
424 | } |
---|
425 | } |
---|
426 | |
---|
427 | PRINT2BUFFER_CHECKED(psize, buffer[my_idx], (size_t)GBS_GLOBAL_STRING_SIZE, templat, parg); |
---|
428 | |
---|
429 | #if defined(TRACE_BUFFER_USAGE) |
---|
430 | printf("Printed into global buffer #%i ('%s')\n", my_idx, buffer[my_idx]); |
---|
431 | #endif /* TRACE_BUFFER_USAGE */ |
---|
432 | |
---|
433 | last_global_string_size = psize; |
---|
434 | |
---|
435 | if (!allow_reuse) { |
---|
436 | idx = my_idx; |
---|
437 | lifetime[idx] = 1; |
---|
438 | } |
---|
439 | #if defined(TRACE_BUFFER_USAGE) |
---|
440 | else { |
---|
441 | printf("Allow reuse of buffer #%i\n", my_idx); |
---|
442 | } |
---|
443 | #endif /* TRACE_BUFFER_USAGE */ |
---|
444 | |
---|
445 | return buffer[my_idx]; |
---|
446 | } |
---|
447 | |
---|
448 | static char *gbs_vglobal_string_copy(const char *templat, va_list parg) { |
---|
449 | GB_CSTR gstr = gbs_vglobal_string(templat, parg, 1); |
---|
450 | return GB_strduplen(gstr, last_global_string_size); |
---|
451 | } |
---|
452 | |
---|
453 | void GBS_reuse_buffer(GB_CSTR global_buffer) { |
---|
454 | /* If you've just shortely used a buffer, you can put it back here */ |
---|
455 | gbs_vglobal_string(global_buffer, 0, -1); |
---|
456 | } |
---|
457 | |
---|
458 | GB_CSTR GBS_global_string(const char *templat, ...) { |
---|
459 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
460 | |
---|
461 | va_list parg; |
---|
462 | GB_CSTR result; |
---|
463 | |
---|
464 | va_start(parg,templat); |
---|
465 | result = gbs_vglobal_string(templat, parg, 0); |
---|
466 | va_end(parg); |
---|
467 | |
---|
468 | return result; |
---|
469 | } |
---|
470 | |
---|
471 | char *GBS_global_string_copy(const char *templat, ...) { |
---|
472 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
473 | |
---|
474 | va_list parg; |
---|
475 | char *result; |
---|
476 | |
---|
477 | va_start(parg,templat); |
---|
478 | result = gbs_vglobal_string_copy(templat, parg); |
---|
479 | va_end(parg); |
---|
480 | |
---|
481 | return result; |
---|
482 | } |
---|
483 | |
---|
484 | #if defined(DEVEL_RALF) |
---|
485 | #warning search for '\b(sprintf)\b\s*\(' and replace by GBS_global_string_to_buffer |
---|
486 | #endif /* DEVEL_RALF */ |
---|
487 | |
---|
488 | const char *GBS_global_string_to_buffer(char *buffer, size_t bufsize, const char *templat, ...) { |
---|
489 | /* goes to header: __ATTR__FORMAT(3) */ |
---|
490 | |
---|
491 | va_list parg; |
---|
492 | int psize; |
---|
493 | |
---|
494 | gb_assert(buffer); |
---|
495 | va_start(parg,templat); |
---|
496 | PRINT2BUFFER_CHECKED(psize, buffer, bufsize, templat, parg); |
---|
497 | va_end(parg); |
---|
498 | |
---|
499 | return buffer; |
---|
500 | } |
---|
501 | |
---|
502 | size_t GBS_last_global_string_size() { |
---|
503 | return last_global_string_size; |
---|
504 | } |
---|
505 | |
---|
506 | char *GBS_string_2_key_with_exclusions(const char *str, const char *additional) |
---|
507 | /* converts any string to a valid key (all chars in 'additional' are additionally allowed) */ |
---|
508 | { |
---|
509 | char buf[GB_KEY_LEN_MAX+1]; |
---|
510 | int i; |
---|
511 | int c; |
---|
512 | for (i=0;i<GB_KEY_LEN_MAX;) { |
---|
513 | c = *(str++); |
---|
514 | if (!c) break; |
---|
515 | |
---|
516 | if (c==' ' || c == '_' ) { |
---|
517 | buf[i++] = '_'; |
---|
518 | } |
---|
519 | else if (isalnum(c) || strchr(additional, c) != 0) { |
---|
520 | buf[i++] = c; |
---|
521 | } |
---|
522 | } |
---|
523 | for (;i<GB_KEY_LEN_MIN;i++) buf[i] = '_'; |
---|
524 | buf[i] = 0; |
---|
525 | return strdup(buf); |
---|
526 | } |
---|
527 | |
---|
528 | char *GBS_string_2_key(const char *str) /* converts any string to a valid key */ |
---|
529 | { |
---|
530 | return GBS_string_2_key_with_exclusions(str, ""); |
---|
531 | } |
---|
532 | |
---|
533 | void gbs_uppercase(char *str) |
---|
534 | { |
---|
535 | char c; |
---|
536 | while( (c=*str) ) { |
---|
537 | if ( (c<='z') && (c>='a')) *str = c - 'a' + 'A'; |
---|
538 | str++; |
---|
539 | } |
---|
540 | } |
---|
541 | |
---|
542 | void gbs_memcopy(char *dest, const char *source, long len) |
---|
543 | { |
---|
544 | long i; |
---|
545 | const char *s; |
---|
546 | char *d; |
---|
547 | |
---|
548 | i = len; |
---|
549 | s = source; |
---|
550 | d = dest; |
---|
551 | if (s < d) { |
---|
552 | s += i; |
---|
553 | d += i; |
---|
554 | while (i--) { |
---|
555 | *(--d) = *(--s); |
---|
556 | } |
---|
557 | } else { |
---|
558 | while (i--) { |
---|
559 | *(d++) = *(s++); |
---|
560 | } |
---|
561 | } |
---|
562 | } |
---|
563 | |
---|
564 | char *gbs_malloc_copy(const char *source, long len) |
---|
565 | { |
---|
566 | char *dest; |
---|
567 | dest = (char *)malloc((size_t)len); |
---|
568 | memcpy(dest,source,(int)len); |
---|
569 | return dest; |
---|
570 | } |
---|
571 | |
---|
572 | GB_ERROR GB_check_key(const char *key) { /* goes to header: __ATTR__USERESULT */ |
---|
573 | /* test whether all characters are letters, numbers or _ */ |
---|
574 | int i; |
---|
575 | long len; |
---|
576 | |
---|
577 | if (!key || key[0] == 0) return GB_export_error("Empty key is not allowed"); |
---|
578 | len = strlen(key); |
---|
579 | if (len>GB_KEY_LEN_MAX) return GB_export_errorf("Invalid key '%s': too long",key); |
---|
580 | if (len < GB_KEY_LEN_MIN) return GB_export_errorf("Invalid key '%s': too short",key); |
---|
581 | |
---|
582 | for (i = 0; key[i]; ++i) { |
---|
583 | char c = key[i]; |
---|
584 | if ( (c>='a') && (c<='z')) continue; |
---|
585 | if ( (c>='A') && (c<='Z')) continue; |
---|
586 | if ( (c>='0') && (c<='9')) continue; |
---|
587 | if ( (c=='_') ) continue; |
---|
588 | return GB_export_errorf("Invalid character '%c' in '%s'; allowed: a-z A-Z 0-9 '_' ", c, key); |
---|
589 | } |
---|
590 | |
---|
591 | return 0; |
---|
592 | } |
---|
593 | GB_ERROR GB_check_link_name(const char *key) { /* goes to header: __ATTR__USERESULT */ |
---|
594 | /* test whether all characters are letters, numbers or _ */ |
---|
595 | int i; |
---|
596 | long len; |
---|
597 | |
---|
598 | if (!key || key[0] == 0) return GB_export_error("Empty key is not allowed"); |
---|
599 | len = strlen(key); |
---|
600 | if (len>GB_KEY_LEN_MAX) return GB_export_errorf("Invalid key '%s': too long",key); |
---|
601 | if (len < 1) return GB_export_errorf("Invalid key '%s': too short",key); // here it differs from GB_check_key |
---|
602 | |
---|
603 | for (i = 0; key[i]; ++i) { |
---|
604 | char c = key[i]; |
---|
605 | if ( (c>='a') && (c<='z')) continue; |
---|
606 | if ( (c>='A') && (c<='Z')) continue; |
---|
607 | if ( (c>='0') && (c<='9')) continue; |
---|
608 | if ( (c=='_') ) continue; |
---|
609 | return GB_export_errorf("Invalid character '%c' in '%s'; allowed: a-z A-Z 0-9 '_' ", c, key); |
---|
610 | } |
---|
611 | |
---|
612 | return 0; |
---|
613 | } |
---|
614 | GB_ERROR GB_check_hkey(const char *key) { /* goes to header: __ATTR__USERESULT */ |
---|
615 | /* test whether all characters are letters, numbers or _ */ |
---|
616 | /* additionally allow '/' and '->' for hierarchical keys */ |
---|
617 | GB_ERROR err = 0; |
---|
618 | |
---|
619 | if (!key || key[0] == 0) { |
---|
620 | err = GB_export_error("Empty key is not allowed"); |
---|
621 | } |
---|
622 | else if (!strpbrk(key, "/-")) { |
---|
623 | err = GB_check_key(key); |
---|
624 | } |
---|
625 | else { |
---|
626 | char *key_copy = strdup(key); |
---|
627 | char *start = key_copy; |
---|
628 | |
---|
629 | if (start[0] == '/') ++start; |
---|
630 | |
---|
631 | while (start && !err) { |
---|
632 | char *key_end = strpbrk(start, "/-"); |
---|
633 | |
---|
634 | if (key_end) { |
---|
635 | char c = *key_end; |
---|
636 | *key_end = 0; |
---|
637 | err = GB_check_key(start); |
---|
638 | *key_end = c; |
---|
639 | |
---|
640 | if (c == '-') { |
---|
641 | if (key_end[1] != '>') { |
---|
642 | err = GB_export_errorf("'>' expected after '-' in '%s'", key); |
---|
643 | } |
---|
644 | start = key_end+2; |
---|
645 | } |
---|
646 | else { |
---|
647 | ad_assert(c == '/'); |
---|
648 | start = key_end+1; |
---|
649 | } |
---|
650 | } |
---|
651 | else { |
---|
652 | err = GB_check_key(start); |
---|
653 | start = 0; |
---|
654 | } |
---|
655 | } |
---|
656 | |
---|
657 | free(key_copy); |
---|
658 | } |
---|
659 | |
---|
660 | return err; |
---|
661 | } |
---|
662 | |
---|
663 | char *gbs_add_path(char *path,char *name) |
---|
664 | { |
---|
665 | long i,len,found; |
---|
666 | char *erg; |
---|
667 | if (!name) return name; |
---|
668 | if (!path) { |
---|
669 | return 0; |
---|
670 | } |
---|
671 | if (*name == '/') return name; |
---|
672 | found =0; |
---|
673 | len = strlen(path); |
---|
674 | for (i=0;i<len;i++) { |
---|
675 | if (path[i] == '/') found = i+1; |
---|
676 | } |
---|
677 | len = found + strlen(name); |
---|
678 | erg = (char *)GB_calloc(sizeof(char),(size_t)(len +1)); |
---|
679 | for (i=0;i<found;i++) { |
---|
680 | erg[i] = path[i]; |
---|
681 | } |
---|
682 | for (i=found;i<len;i++){ |
---|
683 | erg[i] = name[i-found]; |
---|
684 | } |
---|
685 | return erg; |
---|
686 | } |
---|
687 | |
---|
688 | /* --------------------------- */ |
---|
689 | /* escape characters */ |
---|
690 | |
---|
691 | char *GBS_remove_escape(char *com) /* \ is the escape charakter |
---|
692 | */ |
---|
693 | { |
---|
694 | char *result,*s,*d; |
---|
695 | int ch; |
---|
696 | |
---|
697 | s = d = result = strdup(com); |
---|
698 | while ( (ch = *(s++)) ){ |
---|
699 | switch (ch) { |
---|
700 | case '\\': |
---|
701 | ch = *(s++); if (!ch) { s--; break; }; |
---|
702 | switch (ch) { |
---|
703 | case 'n': *(d++) = '\n';break; |
---|
704 | case 't': *(d++) = '\t';break; |
---|
705 | case '0': *(d++) = '\0';break; |
---|
706 | default: *(d++) = ch;break; |
---|
707 | } |
---|
708 | break; |
---|
709 | default: |
---|
710 | *(d++) = ch; |
---|
711 | } |
---|
712 | } |
---|
713 | *d = 0; |
---|
714 | return result; |
---|
715 | } |
---|
716 | |
---|
717 | /******************************************************************************************** |
---|
718 | escape/unescape characters in strings |
---|
719 | ********************************************************************************************/ |
---|
720 | |
---|
721 | char *GBS_escape_string(const char *str, const char *chars_to_escape, char escape_char) { |
---|
722 | // uses a special escape-method, which eliminates all 'chars_to_escape' completely |
---|
723 | // from str (this makes further processing of the string more easy) |
---|
724 | // |
---|
725 | // escape_char is the character used for escaping. For performance reasons it |
---|
726 | // should be a character rarely used in 'str'. |
---|
727 | // |
---|
728 | // 'chars_to_escape' may not contain 'A'-'Z' (these are used for escaping) |
---|
729 | // and it may not be longer than 26 |
---|
730 | // |
---|
731 | // RETURN VALUE : heap copy of escaped string |
---|
732 | |
---|
733 | int len = strlen(str); |
---|
734 | char *buffer = (char*)malloc(2*len+1); |
---|
735 | int j = 0; |
---|
736 | int i; |
---|
737 | |
---|
738 | ad_assert(strlen(chars_to_escape) <= 26); |
---|
739 | ad_assert(strchr(chars_to_escape, escape_char) == 0); // escape_char may not be included in chars_to_escape |
---|
740 | |
---|
741 | for (i = 0; str[i]; ++i) { |
---|
742 | if (str[i] == escape_char) { |
---|
743 | buffer[j++] = escape_char; |
---|
744 | buffer[j++] = escape_char; |
---|
745 | } |
---|
746 | else { |
---|
747 | const char *found = strchr(chars_to_escape, str[i]); |
---|
748 | if (found) { |
---|
749 | buffer[j++] = escape_char; |
---|
750 | buffer[j++] = (found-chars_to_escape+'A'); |
---|
751 | |
---|
752 | ad_assert(found[0]<'A' || found[0]>'Z'); // illegal character in chars_to_escape |
---|
753 | } |
---|
754 | else { |
---|
755 | |
---|
756 | buffer[j++] = str[i]; |
---|
757 | } |
---|
758 | } |
---|
759 | } |
---|
760 | buffer[j] = 0; |
---|
761 | |
---|
762 | return buffer; |
---|
763 | } |
---|
764 | |
---|
765 | char *GBS_unescape_string(const char *str, const char *escaped_chars, char escape_char) { |
---|
766 | // undoes GB_escape_string |
---|
767 | int len = strlen(str); |
---|
768 | char *buffer = (char*)malloc(len+1); |
---|
769 | int j = 0; |
---|
770 | int i; |
---|
771 | |
---|
772 | #if defined(DEBUG) |
---|
773 | int escaped_chars_len = strlen(escaped_chars); |
---|
774 | #endif // DEBUG |
---|
775 | |
---|
776 | ad_assert(strlen(escaped_chars) <= 26); |
---|
777 | ad_assert(strchr(escaped_chars, escape_char) == 0); // escape_char may not be included in chars_to_escape |
---|
778 | |
---|
779 | for (i = 0; str[i]; ++i) { |
---|
780 | if (str[i] == escape_char) { |
---|
781 | if (str[i+1] == escape_char) { |
---|
782 | buffer[j++] = escape_char; |
---|
783 | } |
---|
784 | else { |
---|
785 | int idx = str[i+1]-'A'; |
---|
786 | |
---|
787 | ad_assert(idx >= 0 && idx<escaped_chars_len); |
---|
788 | buffer[j++] = escaped_chars[idx]; |
---|
789 | } |
---|
790 | ++i; |
---|
791 | } |
---|
792 | else { |
---|
793 | buffer[j++] = str[i]; |
---|
794 | } |
---|
795 | } |
---|
796 | buffer[j] = 0; |
---|
797 | |
---|
798 | return buffer; |
---|
799 | } |
---|
800 | |
---|
801 | |
---|
802 | |
---|
803 | /******************************************************************************************** |
---|
804 | String streams |
---|
805 | ********************************************************************************************/ |
---|
806 | |
---|
807 | #if defined(DEBUG) |
---|
808 | // # define DUMP_STRSTRUCT_MEMUSE |
---|
809 | #endif /* DEBUG */ |
---|
810 | |
---|
811 | |
---|
812 | struct GBS_strstruct { |
---|
813 | char *GBS_strcat_data; |
---|
814 | long GBS_strcat_data_size; |
---|
815 | long GBS_strcat_pos; |
---|
816 | }; |
---|
817 | |
---|
818 | static struct GBS_strstruct *last_used = 0; |
---|
819 | |
---|
820 | struct GBS_strstruct *GBS_stropen(long init_size) { /* opens a memory file */ |
---|
821 | struct GBS_strstruct *strstr; |
---|
822 | |
---|
823 | if (last_used && last_used->GBS_strcat_data_size >= init_size) { |
---|
824 | strstr = last_used; |
---|
825 | last_used = 0; |
---|
826 | } |
---|
827 | else { |
---|
828 | #if defined(DUMP_STRSTRUCT_MEMUSE) |
---|
829 | printf("allocating new GBS_strstruct (size = %li)\n", init_size); |
---|
830 | #endif /* DUMP_STRSTRUCT_MEMUSE */ |
---|
831 | strstr = (struct GBS_strstruct *)malloc(sizeof(struct GBS_strstruct)); |
---|
832 | strstr->GBS_strcat_data_size = init_size; |
---|
833 | strstr->GBS_strcat_data = (char *)malloc((size_t)strstr->GBS_strcat_data_size); |
---|
834 | } |
---|
835 | |
---|
836 | strstr->GBS_strcat_pos = 0; |
---|
837 | strstr->GBS_strcat_data[0] = 0; |
---|
838 | |
---|
839 | return strstr; |
---|
840 | } |
---|
841 | |
---|
842 | char *GBS_strclose(struct GBS_strstruct *strstr) { |
---|
843 | /* returns a char* copy of the memory file */ |
---|
844 | |
---|
845 | long length = strstr->GBS_strcat_pos; |
---|
846 | char *str = (char*)malloc(length+1); |
---|
847 | |
---|
848 | gb_assert(str); |
---|
849 | |
---|
850 | memcpy(str, strstr->GBS_strcat_data, length+1); /* copy with 0 */ |
---|
851 | GBS_strforget(strstr); |
---|
852 | |
---|
853 | return str; |
---|
854 | } |
---|
855 | |
---|
856 | void GBS_strforget(struct GBS_strstruct *strstr) { |
---|
857 | if (last_used) { |
---|
858 | if (last_used->GBS_strcat_data_size < strstr->GBS_strcat_data_size) { /* last_used is smaller -> keep this */ |
---|
859 | struct GBS_strstruct *tmp = last_used; |
---|
860 | last_used = strstr; |
---|
861 | strstr = tmp; |
---|
862 | } |
---|
863 | } |
---|
864 | else { |
---|
865 | static short oversized_counter = 0; |
---|
866 | |
---|
867 | if (strstr->GBS_strcat_pos*10 < strstr->GBS_strcat_data_size) oversized_counter++; |
---|
868 | else oversized_counter = 0; |
---|
869 | |
---|
870 | if (oversized_counter<10) { |
---|
871 | // keep strstruct for next call |
---|
872 | last_used = strstr; |
---|
873 | strstr = 0; |
---|
874 | } |
---|
875 | // otherwise the current strstruct was oversized 10 times -> free it |
---|
876 | } |
---|
877 | |
---|
878 | if (strstr) { |
---|
879 | #if defined(DUMP_STRSTRUCT_MEMUSE) |
---|
880 | printf("freeing GBS_strstruct (size = %li)\n", strstr->GBS_strcat_data_size); |
---|
881 | #endif /* DUMP_STRSTRUCT_MEMUSE */ |
---|
882 | free(strstr->GBS_strcat_data); |
---|
883 | free(strstr); |
---|
884 | } |
---|
885 | } |
---|
886 | |
---|
887 | GB_BUFFER GBS_mempntr(struct GBS_strstruct *strstr) { |
---|
888 | /* returns the memory file */ |
---|
889 | return strstr->GBS_strcat_data; |
---|
890 | } |
---|
891 | |
---|
892 | long GBS_memoffset(struct GBS_strstruct *strstr) { |
---|
893 | /* returns the offset into the memory file */ |
---|
894 | return strstr->GBS_strcat_pos; |
---|
895 | } |
---|
896 | |
---|
897 | void GBS_str_cut_tail(struct GBS_strstruct *strstr, int byte_count){ |
---|
898 | /* Removes byte_count characters at the tail of a memfile */ |
---|
899 | strstr->GBS_strcat_pos -= byte_count; |
---|
900 | if (strstr->GBS_strcat_pos < 0) strstr->GBS_strcat_pos = 0; |
---|
901 | strstr->GBS_strcat_data[strstr->GBS_strcat_pos] = 0; |
---|
902 | } |
---|
903 | |
---|
904 | static void gbs_strensure_mem(struct GBS_strstruct *strstr,long len) { |
---|
905 | if (strstr->GBS_strcat_pos + len + 2 >= strstr->GBS_strcat_data_size) { |
---|
906 | strstr->GBS_strcat_data_size = (strstr->GBS_strcat_pos+len+2)*3/2; |
---|
907 | strstr->GBS_strcat_data = (char *)realloc((MALLOC_T)strstr->GBS_strcat_data,(size_t)strstr->GBS_strcat_data_size); |
---|
908 | #if defined(DUMP_STRSTRUCT_MEMUSE) |
---|
909 | printf("re-allocated GBS_strstruct to size = %li\n", strstr->GBS_strcat_data_size); |
---|
910 | #endif /* DUMP_STRSTRUCT_MEMUSE */ |
---|
911 | } |
---|
912 | } |
---|
913 | |
---|
914 | void GBS_strncat(struct GBS_strstruct *strstr, const char *ptr, size_t len) { |
---|
915 | /* append some bytes string to strstruct |
---|
916 | * (caution : copies zero byte and things behind!) |
---|
917 | */ |
---|
918 | if (len>0) { |
---|
919 | gbs_strensure_mem(strstr,len+2); |
---|
920 | memcpy(strstr->GBS_strcat_data+strstr->GBS_strcat_pos,ptr, len); |
---|
921 | strstr->GBS_strcat_pos += len; |
---|
922 | strstr->GBS_strcat_data[strstr->GBS_strcat_pos] = 0; |
---|
923 | } |
---|
924 | } |
---|
925 | |
---|
926 | void GBS_strcat(struct GBS_strstruct *strstr, const char *ptr) { |
---|
927 | /* append string to strstruct */ |
---|
928 | GBS_strncat(strstr, ptr, strlen(ptr)); |
---|
929 | } |
---|
930 | |
---|
931 | |
---|
932 | |
---|
933 | void GBS_strnprintf(struct GBS_strstruct *strstr, long len, const char *templat, ...) { |
---|
934 | /* goes to header: __ATTR__FORMAT(3) */ |
---|
935 | char *buffer; |
---|
936 | int psize; |
---|
937 | va_list parg; |
---|
938 | |
---|
939 | va_start(parg,templat); |
---|
940 | gbs_strensure_mem(strstr,len+2); |
---|
941 | |
---|
942 | buffer = strstr->GBS_strcat_data+strstr->GBS_strcat_pos; |
---|
943 | |
---|
944 | #ifdef LINUX |
---|
945 | psize = vsnprintf(buffer,len,templat,parg); |
---|
946 | #else |
---|
947 | psize = vsprintf(buffer,templat,parg); |
---|
948 | #endif |
---|
949 | |
---|
950 | assert_or_exit(psize >= 0 && psize <= len); |
---|
951 | strstr->GBS_strcat_pos += psize; |
---|
952 | } |
---|
953 | |
---|
954 | void GBS_chrcat(struct GBS_strstruct *strstr,char ch) { |
---|
955 | gbs_strensure_mem(strstr, 1); |
---|
956 | strstr->GBS_strcat_data[strstr->GBS_strcat_pos++] = ch; |
---|
957 | strstr->GBS_strcat_data[strstr->GBS_strcat_pos] = 0; |
---|
958 | } |
---|
959 | |
---|
960 | void GBS_intcat(struct GBS_strstruct *strstr,long val) { |
---|
961 | char buffer[200]; |
---|
962 | long len = sprintf(buffer,"%li",val); |
---|
963 | GBS_strncat(strstr, buffer, len); |
---|
964 | } |
---|
965 | |
---|
966 | void GBS_floatcat(struct GBS_strstruct *strstr,double val) { |
---|
967 | char buffer[200]; |
---|
968 | long len = sprintf(buffer,"%f",val); |
---|
969 | GBS_strncat(strstr, buffer, len); |
---|
970 | } |
---|
971 | |
---|
972 | char *GBS_eval_env(GB_CSTR p){ |
---|
973 | GB_ERROR error = 0; |
---|
974 | GB_CSTR ka; |
---|
975 | void *out = GBS_stropen(1000); |
---|
976 | |
---|
977 | while ((ka = GBS_find_string(p,"$(",0))) { |
---|
978 | GB_CSTR kz = strchr(ka,')'); |
---|
979 | if (!kz) { |
---|
980 | error = GBS_global_string("missing ')' for envvar '%s'", p); |
---|
981 | break; |
---|
982 | } |
---|
983 | else { |
---|
984 | char *envvar = GB_strpartdup(ka+2, kz-1); |
---|
985 | int len = ka-p; |
---|
986 | |
---|
987 | if (len) GBS_strncat(out, p, len); |
---|
988 | |
---|
989 | GB_CSTR genv = GB_getenv(envvar); |
---|
990 | if (genv) GBS_strcat(out, genv); |
---|
991 | |
---|
992 | p = kz+1; |
---|
993 | free(envvar); |
---|
994 | } |
---|
995 | } |
---|
996 | |
---|
997 | if (error) { |
---|
998 | GB_export_error(error); |
---|
999 | free(GBS_strclose(out)); |
---|
1000 | return 0; |
---|
1001 | } |
---|
1002 | |
---|
1003 | GBS_strcat(out, p); // copy rest |
---|
1004 | return GBS_strclose(out); |
---|
1005 | } |
---|
1006 | |
---|
1007 | char *GBS_find_lib_file(const char *filename, const char *libprefix, int warn_when_not_found) { |
---|
1008 | /* Searches files in current dir, $HOME, $ARBHOME/lib/libprefix */ |
---|
1009 | |
---|
1010 | char *result = 0; |
---|
1011 | |
---|
1012 | if (GB_is_readablefile(filename)) { |
---|
1013 | result = strdup(filename); |
---|
1014 | } |
---|
1015 | else { |
---|
1016 | const char *slash = strrchr(filename, '/'); // look for last slash |
---|
1017 | |
---|
1018 | if (slash && filename[0] != '.') { // have absolute path |
---|
1019 | filename = slash+1; // only use filename part |
---|
1020 | slash = 0; |
---|
1021 | } |
---|
1022 | |
---|
1023 | const char *fileInHome = GB_concat_full_path(GB_getenvHOME(), filename); |
---|
1024 | |
---|
1025 | if (fileInHome && GB_is_readablefile(fileInHome)) { |
---|
1026 | result = strdup(fileInHome); |
---|
1027 | } |
---|
1028 | else { |
---|
1029 | if (slash) filename = slash+1; // now use filename only, even if path starts with '.' |
---|
1030 | |
---|
1031 | const char *fileInLib = GB_path_in_ARBLIB(libprefix, filename); |
---|
1032 | |
---|
1033 | if (fileInLib && GB_is_readablefile(fileInLib)) { |
---|
1034 | result = strdup(fileInLib); |
---|
1035 | } |
---|
1036 | else { |
---|
1037 | if (warn_when_not_found) { |
---|
1038 | GB_warningf("Don't know where to find '%s'\n" |
---|
1039 | " searched in '.'\n" |
---|
1040 | " searched in $(HOME) (for '%s')\n" |
---|
1041 | " searched in $(ARBHOME)/lib/%s (for '%s')\n", |
---|
1042 | filename, fileInHome, libprefix, fileInLib); |
---|
1043 | } |
---|
1044 | } |
---|
1045 | } |
---|
1046 | } |
---|
1047 | |
---|
1048 | return result; |
---|
1049 | } |
---|
1050 | |
---|
1051 | |
---|
1052 | |
---|
1053 | /* ******************************************************************************************* |
---|
1054 | some simple find procedures |
---|
1055 | ********************************************************************************************/ |
---|
1056 | |
---|
1057 | char **GBS_read_dir(const char *dir, const char *mask) { |
---|
1058 | /* Return names of files in directory 'dir'. |
---|
1059 | * Filter through 'mask': |
---|
1060 | * - mask == NULL -> return all files |
---|
1061 | * - in format '/expr/' -> use regular expression (case sensitive) |
---|
1062 | * - else it does a simple string match with wildcards ("?*") |
---|
1063 | * |
---|
1064 | * Result is a NULL terminated array of char* (sorted alphanumerically) |
---|
1065 | * Use GBT_free_names() to free the result. |
---|
1066 | * |
---|
1067 | * In case of error, result is NULL and error is exported |
---|
1068 | * |
---|
1069 | * Special case: If 'dir' is the name of a file, return an array with file as only element |
---|
1070 | */ |
---|
1071 | |
---|
1072 | gb_assert(dir); // dir == NULL was allowed before 12/2008, forbidden now! |
---|
1073 | |
---|
1074 | char *fulldir = nulldup(GB_get_full_path(dir)); |
---|
1075 | DIR *dirstream = opendir(fulldir); |
---|
1076 | char **names = NULL; |
---|
1077 | |
---|
1078 | if (!dirstream) { |
---|
1079 | if (GB_is_readablefile(fulldir)) { |
---|
1080 | names = malloc(2*sizeof(*names)); |
---|
1081 | names[0] = strdup(fulldir); |
---|
1082 | names[1] = NULL; |
---|
1083 | } |
---|
1084 | else { |
---|
1085 | char *lslash = strrchr(fulldir, '/'); |
---|
1086 | |
---|
1087 | if (lslash) { |
---|
1088 | char *name; |
---|
1089 | lslash[0] = 0; |
---|
1090 | name = lslash+1; |
---|
1091 | if (GB_is_directory(fulldir)) { |
---|
1092 | names = GBS_read_dir(fulldir, name); |
---|
1093 | } |
---|
1094 | lslash[0] = '/'; |
---|
1095 | } |
---|
1096 | |
---|
1097 | if (!names) GB_export_errorf("can't read directory '%s'", fulldir); |
---|
1098 | } |
---|
1099 | } |
---|
1100 | else { |
---|
1101 | if (mask == NULL) mask = "*"; |
---|
1102 | |
---|
1103 | GBS_MATCHER *matcher = GBS_compile_matcher(mask, GB_MIND_CASE); |
---|
1104 | if (matcher) { |
---|
1105 | int allocated = 100; |
---|
1106 | int entries = 0; |
---|
1107 | names = malloc(100*sizeof(*names)); |
---|
1108 | |
---|
1109 | struct dirent *entry; |
---|
1110 | while ((entry = readdir(dirstream)) != 0) { |
---|
1111 | const char *name = entry->d_name; |
---|
1112 | |
---|
1113 | if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { |
---|
1114 | ; // skip '.' and '..' |
---|
1115 | } |
---|
1116 | else { |
---|
1117 | if (GBS_string_matches_regexp(name, matcher)) { |
---|
1118 | const char *full = GB_concat_path(fulldir, name); |
---|
1119 | if (!GB_is_directory(full)) { // skip directories |
---|
1120 | if (entries == allocated) { |
---|
1121 | allocated += allocated>>1; // * 1.5 |
---|
1122 | names = realloc(names, allocated*sizeof(*names)); |
---|
1123 | } |
---|
1124 | names[entries++] = strdup(full); |
---|
1125 | } |
---|
1126 | } |
---|
1127 | } |
---|
1128 | } |
---|
1129 | |
---|
1130 | names = realloc(names, (entries+1)*sizeof(*names)); |
---|
1131 | names[entries] = NULL; |
---|
1132 | |
---|
1133 | GB_sort((void**)names, 0, entries, GB_string_comparator, 0); |
---|
1134 | |
---|
1135 | GBS_free_matcher(matcher); |
---|
1136 | } |
---|
1137 | |
---|
1138 | closedir(dirstream); |
---|
1139 | } |
---|
1140 | |
---|
1141 | free(fulldir); |
---|
1142 | |
---|
1143 | return names; |
---|
1144 | } |
---|
1145 | |
---|
1146 | long GBS_gcgchecksum( const char *seq ) |
---|
1147 | /* GCGchecksum */ |
---|
1148 | { |
---|
1149 | long i; |
---|
1150 | long check = 0; |
---|
1151 | long count = 0; |
---|
1152 | long seqlen = strlen(seq); |
---|
1153 | |
---|
1154 | for (i = 0; i < seqlen; i++) { |
---|
1155 | count++; |
---|
1156 | check += count * toupper(seq[i]); |
---|
1157 | if (count == 57) count = 0; |
---|
1158 | } |
---|
1159 | check %= 10000; |
---|
1160 | |
---|
1161 | return check; |
---|
1162 | } |
---|
1163 | |
---|
1164 | /* Table of CRC-32's of all single byte values (made by makecrc.c of ZIP source) */ |
---|
1165 | const uint32_t crctab[] = { |
---|
1166 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, |
---|
1167 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, |
---|
1168 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, |
---|
1169 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, |
---|
1170 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, |
---|
1171 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, |
---|
1172 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, |
---|
1173 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, |
---|
1174 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, |
---|
1175 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, |
---|
1176 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, |
---|
1177 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, |
---|
1178 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, |
---|
1179 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, |
---|
1180 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, |
---|
1181 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, |
---|
1182 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, |
---|
1183 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, |
---|
1184 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, |
---|
1185 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, |
---|
1186 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, |
---|
1187 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, |
---|
1188 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, |
---|
1189 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, |
---|
1190 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, |
---|
1191 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, |
---|
1192 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, |
---|
1193 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, |
---|
1194 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, |
---|
1195 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, |
---|
1196 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, |
---|
1197 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, |
---|
1198 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, |
---|
1199 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, |
---|
1200 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, |
---|
1201 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, |
---|
1202 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, |
---|
1203 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, |
---|
1204 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, |
---|
1205 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, |
---|
1206 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, |
---|
1207 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, |
---|
1208 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, |
---|
1209 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, |
---|
1210 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, |
---|
1211 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, |
---|
1212 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, |
---|
1213 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, |
---|
1214 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, |
---|
1215 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, |
---|
1216 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, |
---|
1217 | 0x2d02ef8dL |
---|
1218 | }; |
---|
1219 | |
---|
1220 | uint32_t GB_checksum(const char *seq, long length, int ignore_case , const char *exclude) /* RALF: 02-12-96 */ |
---|
1221 | /* |
---|
1222 | * CRC32checksum: modified from CRC-32 algorithm found in ZIP compression source |
---|
1223 | * if ignore_case == true -> treat all characters as uppercase-chars (applies to exclude too) |
---|
1224 | */ |
---|
1225 | { |
---|
1226 | unsigned long c = 0xffffffffL; |
---|
1227 | long n = length; |
---|
1228 | int i; |
---|
1229 | int tab[256]; |
---|
1230 | |
---|
1231 | for (i=0;i<256;i++) { |
---|
1232 | tab[i] = ignore_case ? toupper(i) : i; |
---|
1233 | } |
---|
1234 | |
---|
1235 | if (exclude) { |
---|
1236 | while (1) { |
---|
1237 | int k = *(unsigned char *)exclude++; |
---|
1238 | if (!k) break; |
---|
1239 | tab[k] = 0; |
---|
1240 | if (ignore_case) tab[toupper(k)] = tab[tolower(k)] = 0; |
---|
1241 | } |
---|
1242 | } |
---|
1243 | |
---|
1244 | while (n--) { |
---|
1245 | i = tab[*(const unsigned char *)seq++]; |
---|
1246 | if (i) { |
---|
1247 | c = crctab[((int) c ^ i) & 0xff] ^ (c >> 8); |
---|
1248 | } |
---|
1249 | } |
---|
1250 | c = c ^ 0xffffffffL; |
---|
1251 | return c; |
---|
1252 | } |
---|
1253 | |
---|
1254 | uint32_t GBS_checksum(const char *seq, int ignore_case, const char *exclude) |
---|
1255 | /* if 'ignore_case' == true -> treat all characters as uppercase-chars (applies to 'exclude' too) */ |
---|
1256 | { |
---|
1257 | return GB_checksum(seq, strlen(seq), ignore_case, exclude); |
---|
1258 | } |
---|
1259 | |
---|
1260 | /* extract all words in a text that: |
---|
1261 | 1. minlen < 1.0 contain more than minlen*len_of_text characters that also exists in chars |
---|
1262 | 2. minlen > 1.0 contain more than minlen characters that also exists in chars |
---|
1263 | */ |
---|
1264 | |
---|
1265 | char *GBS_extract_words( const char *source,const char *chars, float minlen, GB_BOOL sort_output ) { |
---|
1266 | char *s = strdup(source); |
---|
1267 | char **ps = (char **)GB_calloc(sizeof(char *), (strlen(source)>>1) + 1); |
---|
1268 | void *strstruct = GBS_stropen(1000); |
---|
1269 | char *f = s; |
---|
1270 | int count = 0; |
---|
1271 | char *p; |
---|
1272 | char *h; |
---|
1273 | int cnt; |
---|
1274 | int len; |
---|
1275 | int iminlen = (int) (minlen+.5); |
---|
1276 | |
---|
1277 | while ( (p = strtok(f," \t,;:|")) ) { |
---|
1278 | f = 0; |
---|
1279 | cnt = 0; |
---|
1280 | len = strlen(p); |
---|
1281 | for (h=p;*h;h++) { |
---|
1282 | if (strchr(chars,*h)) cnt++; |
---|
1283 | } |
---|
1284 | |
---|
1285 | if (minlen == 1.0) { |
---|
1286 | if (cnt != len) continue; |
---|
1287 | }else if (minlen > 1.0) { |
---|
1288 | if (cnt < iminlen) continue; |
---|
1289 | }else{ |
---|
1290 | if (len < 3 || cnt < minlen*len) continue; |
---|
1291 | } |
---|
1292 | ps[count] = p; |
---|
1293 | count ++; |
---|
1294 | } |
---|
1295 | if (sort_output) { |
---|
1296 | GB_sort((void **)ps, 0, count, GB_string_comparator, 0); |
---|
1297 | } |
---|
1298 | for (cnt = 0;cnt<count;cnt++) { |
---|
1299 | if (cnt) { |
---|
1300 | GBS_chrcat(strstruct,' '); |
---|
1301 | } |
---|
1302 | GBS_strcat(strstruct,ps[cnt]); |
---|
1303 | } |
---|
1304 | |
---|
1305 | free((char *)ps); |
---|
1306 | free(s); |
---|
1307 | return GBS_strclose(strstruct); |
---|
1308 | } |
---|
1309 | |
---|
1310 | |
---|
1311 | size_t GBS_shorten_repeated_data(char *data) { |
---|
1312 | // shortens repeats in 'data' |
---|
1313 | // This function modifies 'data'!! |
---|
1314 | // e.g. "..............................ACGT....................TGCA" |
---|
1315 | // -> ".{30}ACGT.{20}TGCA" |
---|
1316 | |
---|
1317 | #if defined(DEBUG) |
---|
1318 | size_t orgLen = strlen(data); |
---|
1319 | #endif // DEBUG |
---|
1320 | char *dataStart = data; |
---|
1321 | char *dest = data; |
---|
1322 | size_t repeat = 1; |
---|
1323 | char last = *data++; |
---|
1324 | |
---|
1325 | do { |
---|
1326 | char curr = *data++; |
---|
1327 | if (curr == last) { |
---|
1328 | repeat++; |
---|
1329 | } |
---|
1330 | else { |
---|
1331 | if (repeat >= 10) { |
---|
1332 | dest += sprintf(dest, "%c{%zu}", last, repeat); // insert repeat count |
---|
1333 | } |
---|
1334 | else { |
---|
1335 | size_t r; |
---|
1336 | for (r = 0; r<repeat; r++) *dest++ = last; // insert plain |
---|
1337 | } |
---|
1338 | last = curr; |
---|
1339 | repeat = 1; |
---|
1340 | } |
---|
1341 | } |
---|
1342 | while (last); |
---|
1343 | |
---|
1344 | *dest = 0; |
---|
1345 | |
---|
1346 | #if defined(DEBUG) |
---|
1347 | |
---|
1348 | ad_assert(strlen(dataStart) <= orgLen); |
---|
1349 | #endif // DEBUG |
---|
1350 | return dest-dataStart; |
---|
1351 | } |
---|
1352 | |
---|
1353 | /* ----------------------- */ |
---|
1354 | /* Error handler */ |
---|
1355 | |
---|
1356 | static void gb_error_to_stderr(const char *msg) { |
---|
1357 | fprintf(stderr, "%s\n", msg); |
---|
1358 | } |
---|
1359 | |
---|
1360 | static gb_error_handler_type gb_error_handler = gb_error_to_stderr; |
---|
1361 | |
---|
1362 | NOT4PERL void GB_install_error_handler(gb_error_handler_type aw_message_handler){ |
---|
1363 | gb_error_handler = aw_message_handler; |
---|
1364 | } |
---|
1365 | |
---|
1366 | /* --------------------- */ |
---|
1367 | /* Backtracing */ |
---|
1368 | |
---|
1369 | #define MAX_BACKTRACE 66 |
---|
1370 | |
---|
1371 | void GBK_dump_backtrace(FILE *out, GB_ERROR error) { |
---|
1372 | void *array[MAX_BACKTRACE]; |
---|
1373 | size_t size = backtrace(array, MAX_BACKTRACE); // get void*'s for all entries on the stack |
---|
1374 | |
---|
1375 | if (!out) out = stderr; |
---|
1376 | |
---|
1377 | // print out all the frames to out |
---|
1378 | fprintf(out, "\n-------------------- ARB-backtrace for '%s':\n", error); |
---|
1379 | backtrace_symbols_fd(array, size, fileno(out)); |
---|
1380 | if (size == MAX_BACKTRACE) fputs("[stack truncated to avoid deadlock]\n", out); |
---|
1381 | fputs("-------------------- End of backtrace\n", out); |
---|
1382 | fflush(out); |
---|
1383 | } |
---|
1384 | |
---|
1385 | /* ----------------------- */ |
---|
1386 | /* catch SIGSEGV */ |
---|
1387 | |
---|
1388 | static void sigsegv_handler_exit(int sig) { |
---|
1389 | fprintf(stderr, "[Terminating with signal %i]\n", sig); |
---|
1390 | exit(EXIT_FAILURE); |
---|
1391 | } |
---|
1392 | void sigsegv_handler_dump(int sig) { |
---|
1393 | /* sigsegv_handler is intentionally global, to show it in backtrace! */ |
---|
1394 | GBK_dump_backtrace(stderr, GBS_global_string("received signal %i", sig)); |
---|
1395 | sigsegv_handler_exit(sig); |
---|
1396 | } |
---|
1397 | |
---|
1398 | void GBK_install_SIGSEGV_handler(GB_BOOL install) { |
---|
1399 | signal(SIGSEGV, install ? sigsegv_handler_dump : sigsegv_handler_exit); |
---|
1400 | } |
---|
1401 | |
---|
1402 | /* ------------------------------------------- */ |
---|
1403 | /* Error/notification functions */ |
---|
1404 | |
---|
1405 | void GB_internal_error(const char *message) { |
---|
1406 | /* Use GB_internal_error, when something goes badly wrong |
---|
1407 | * but you want to give the user a chance to save his database |
---|
1408 | * |
---|
1409 | * Note: it's NOT recommended to use this function! |
---|
1410 | */ |
---|
1411 | |
---|
1412 | char *full_message = GBS_global_string_copy("Internal ARB Error: %s", message); |
---|
1413 | gb_error_handler(full_message); |
---|
1414 | gb_error_handler("ARB is most likely unstable now (due to this error).\n" |
---|
1415 | "If you've made changes to the database, consider to save it using a different name.\n" |
---|
1416 | "Try to fix the cause of the error and restart ARB."); |
---|
1417 | |
---|
1418 | #ifdef ASSERTION_USED |
---|
1419 | fputs(full_message, stderr); |
---|
1420 | gb_assert(0); // internal errors shall not happen, go fix it |
---|
1421 | #else |
---|
1422 | GBK_dump_backtrace(stderr, full_message); |
---|
1423 | #endif |
---|
1424 | |
---|
1425 | free(full_message); |
---|
1426 | } |
---|
1427 | |
---|
1428 | void GB_internal_errorf(const char *templat, ...) { |
---|
1429 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
1430 | va_list parg; |
---|
1431 | |
---|
1432 | va_start(parg, templat); |
---|
1433 | GB_CSTR message = gbs_vglobal_string(templat, parg, 0); |
---|
1434 | va_end(parg); |
---|
1435 | |
---|
1436 | GB_internal_error(message); |
---|
1437 | } |
---|
1438 | |
---|
1439 | void GBK_terminate(const char *error) { |
---|
1440 | |
---|
1441 | /* GBK_terminate is the emergency exit! |
---|
1442 | * only used if no other way to recover |
---|
1443 | */ |
---|
1444 | |
---|
1445 | fprintf(stderr, "Error: '%s'\n", error); |
---|
1446 | fputs("Can't continue - terminating..\n", stderr); |
---|
1447 | GBK_dump_backtrace(stderr, "GBK_terminate"); |
---|
1448 | |
---|
1449 | fflush(stderr); |
---|
1450 | ARB_SIGSEGV(0); // GBK_terminate shall not be called, fix reason for call (this will crash in RELEASE version) |
---|
1451 | exit(EXIT_FAILURE); |
---|
1452 | } |
---|
1453 | |
---|
1454 | void GBK_terminatef(const char *templat, ...) { |
---|
1455 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
1456 | va_list parg; |
---|
1457 | |
---|
1458 | va_start(parg,templat); |
---|
1459 | GB_CSTR error = gbs_vglobal_string(templat, parg, 0); |
---|
1460 | va_end(parg); |
---|
1461 | |
---|
1462 | GBK_terminate(error); |
---|
1463 | } |
---|
1464 | |
---|
1465 | GB_ERROR GBK_assert_msg(const char *assertion, const char *file, int linenr) { |
---|
1466 | #define BUFSIZE 1000 |
---|
1467 | static char *buffer = 0; |
---|
1468 | const char *result = 0; |
---|
1469 | int old_size = last_global_string_size; |
---|
1470 | |
---|
1471 | if (!buffer) buffer = (char *)malloc(BUFSIZE); |
---|
1472 | result = GBS_global_string_to_buffer(buffer, BUFSIZE, "assertion '%s' failed in %s #%i", assertion, file, linenr); |
---|
1473 | |
---|
1474 | last_global_string_size = old_size; |
---|
1475 | |
---|
1476 | return result; |
---|
1477 | #undef BUFSIZE |
---|
1478 | } |
---|
1479 | |
---|
1480 | void GB_warning(const char *message) { |
---|
1481 | /* If program uses GUI, the message is printed via aw_message, otherwise it goes to stdout |
---|
1482 | * see also : GB_information |
---|
1483 | */ |
---|
1484 | |
---|
1485 | if (gb_warning_func) { |
---|
1486 | gb_warning_func(message); |
---|
1487 | } |
---|
1488 | else { |
---|
1489 | fputs(message, stdout); |
---|
1490 | fputc('\n', stdout); |
---|
1491 | } |
---|
1492 | } |
---|
1493 | void GB_warningf(const char *templat, ...) { |
---|
1494 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
1495 | |
---|
1496 | va_list parg; |
---|
1497 | |
---|
1498 | va_start(parg,templat); |
---|
1499 | char *message = gbs_vglobal_string_copy(templat, parg); |
---|
1500 | va_end(parg); |
---|
1501 | |
---|
1502 | GB_warning(message); |
---|
1503 | free(message); |
---|
1504 | } |
---|
1505 | |
---|
1506 | NOT4PERL void GB_install_warning(gb_warning_func_type warn){ |
---|
1507 | gb_warning_func = warn; |
---|
1508 | } |
---|
1509 | |
---|
1510 | void GB_information(const char *message) { |
---|
1511 | if (gb_information_func) { |
---|
1512 | gb_information_func(message); |
---|
1513 | } |
---|
1514 | else { |
---|
1515 | fputs(message, stdout); |
---|
1516 | fputc('\n', stdout); |
---|
1517 | } |
---|
1518 | } |
---|
1519 | void GB_informationf(const char *templat, ...) { |
---|
1520 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
1521 | |
---|
1522 | /* this message is always printed to stdout (regardless whether program uses GUI or not) |
---|
1523 | * see also : GB_warning |
---|
1524 | */ |
---|
1525 | |
---|
1526 | va_list parg; |
---|
1527 | |
---|
1528 | va_start(parg,templat); |
---|
1529 | char *message = gbs_vglobal_string_copy(templat, parg); |
---|
1530 | va_end(parg); |
---|
1531 | |
---|
1532 | GB_information(message); |
---|
1533 | free(message); |
---|
1534 | } |
---|
1535 | |
---|
1536 | NOT4PERL void GB_install_information(gb_information_func_type info){ |
---|
1537 | gb_information_func = info; |
---|
1538 | } |
---|
1539 | |
---|
1540 | |
---|
1541 | int GB_status( double val) { |
---|
1542 | int result = 0; |
---|
1543 | |
---|
1544 | /* if program uses GUI this uses aw_status(), |
---|
1545 | * otherwise it uses simple status to stdout |
---|
1546 | * |
---|
1547 | * return value : 0 = ok, 1 = userAbort |
---|
1548 | */ |
---|
1549 | |
---|
1550 | if ( gb_status_func ) { |
---|
1551 | result = gb_status_func(val); |
---|
1552 | } |
---|
1553 | else { |
---|
1554 | char buffer[100]; |
---|
1555 | int i; |
---|
1556 | static int lastv = 0; |
---|
1557 | int v = (int)(val*80); |
---|
1558 | if (v == lastv) return 0; |
---|
1559 | lastv = v; |
---|
1560 | for (i=0;i<v;i++) buffer[i] = '+'; |
---|
1561 | for (;i<80;i++) buffer[i] = '-'; |
---|
1562 | buffer[i] = 0; |
---|
1563 | fprintf(stdout,"%s\n",buffer); |
---|
1564 | } |
---|
1565 | return result; |
---|
1566 | } |
---|
1567 | |
---|
1568 | NOT4PERL void GB_install_status(gb_status_func_type func){ |
---|
1569 | gb_status_func = func; |
---|
1570 | } |
---|
1571 | |
---|
1572 | |
---|
1573 | int GB_status2( const char *templat, ... ) { |
---|
1574 | /* goes to header: __ATTR__FORMAT(1) */ |
---|
1575 | |
---|
1576 | /* return value : 0 = ok, 1 = userAbort */ |
---|
1577 | |
---|
1578 | va_list parg; |
---|
1579 | |
---|
1580 | if ( gb_status_func2 ) { |
---|
1581 | char buffer[4000];memset(&buffer[0],0,4000); |
---|
1582 | va_start(parg,templat); |
---|
1583 | vsprintf(buffer,templat,parg); |
---|
1584 | return gb_status_func2(buffer); |
---|
1585 | }else{ |
---|
1586 | va_start(parg,templat); |
---|
1587 | vfprintf(stdout,templat,parg); |
---|
1588 | fprintf(stdout,"\n"); |
---|
1589 | return 0; |
---|
1590 | } |
---|
1591 | } |
---|
1592 | |
---|
1593 | NOT4PERL void GB_install_status2(gb_status_func2_type func2){ |
---|
1594 | gb_status_func2 = func2; |
---|
1595 | } |
---|
1596 | |
---|
1597 | /* ------------------------------------------- */ |
---|
1598 | /* helper function for tagged fields */ |
---|
1599 | |
---|
1600 | static GB_ERROR g_bs_add_value_tag_to_hash(GBDATA *gb_main, GB_HASH *hash, char *tag, char *value,const char *rtag, const char *srt, const char *aci, GBDATA *gbd) { |
---|
1601 | char *p; |
---|
1602 | GB_HASH *sh; |
---|
1603 | char *to_free = 0; |
---|
1604 | if (rtag && strcmp(tag,rtag) == 0){ |
---|
1605 | if (srt) { |
---|
1606 | value = to_free = GBS_string_eval(value,srt,gbd); |
---|
1607 | } |
---|
1608 | else if (aci) { |
---|
1609 | value = to_free = GB_command_interpreter(gb_main,value,aci,gbd, 0); |
---|
1610 | } |
---|
1611 | if (!value) return GB_await_error(); |
---|
1612 | } |
---|
1613 | |
---|
1614 | p=value; while ( (p = strchr(p,'[')) ) *p = '{'; /* replace all '[' by '{' */ |
---|
1615 | p=value; while ( (p = strchr(p,']')) ) *p = '}'; /* replace all ']' by '}' */ |
---|
1616 | |
---|
1617 | sh = (GB_HASH *)GBS_read_hash(hash,value); |
---|
1618 | if (!sh){ |
---|
1619 | sh = GBS_create_hash(10, GB_IGNORE_CASE); /* Tags are case independent */ |
---|
1620 | GBS_write_hash(hash,value,(long)sh); |
---|
1621 | } |
---|
1622 | |
---|
1623 | GBS_write_hash(sh,tag,1); |
---|
1624 | if (to_free) free(to_free); |
---|
1625 | return 0; |
---|
1626 | } |
---|
1627 | |
---|
1628 | |
---|
1629 | static GB_ERROR g_bs_convert_string_to_tagged_hash(GB_HASH *hash, char *s,char *default_tag,const char *del, |
---|
1630 | GBDATA *gb_main, const char *rtag,const char *srt, const char *aci, GBDATA *gbd){ |
---|
1631 | char *se; /* string end */ |
---|
1632 | char *sa; /* string anfang and tag end */ |
---|
1633 | char *ts; /* tag start */ |
---|
1634 | char *t; |
---|
1635 | GB_ERROR error = 0; |
---|
1636 | while (s && s[0]) { |
---|
1637 | ts = strchr(s,'['); |
---|
1638 | if (!ts){ |
---|
1639 | error = g_bs_add_value_tag_to_hash(gb_main,hash,default_tag,s,rtag,srt,aci,gbd); /* no tag found, use default tag */ |
---|
1640 | if (error) break; |
---|
1641 | break; |
---|
1642 | }else{ |
---|
1643 | *(ts++) = 0; |
---|
1644 | } |
---|
1645 | sa = strchr(ts,']'); |
---|
1646 | if (sa){ |
---|
1647 | *sa++ = 0; |
---|
1648 | while (*sa == ' ') sa++; |
---|
1649 | }else{ |
---|
1650 | error = g_bs_add_value_tag_to_hash(gb_main,hash,default_tag,s,rtag,srt,aci,gbd); /* no tag found, use default tag */ |
---|
1651 | if (error) break; |
---|
1652 | break; |
---|
1653 | } |
---|
1654 | se = strchr(sa,'['); |
---|
1655 | if (se) { |
---|
1656 | while (se>sa && se[-1] == ' ') se--; |
---|
1657 | *(se++) = 0; |
---|
1658 | } |
---|
1659 | for (t = strtok(ts,","); t; t = strtok(0,",")){ |
---|
1660 | if (del && strcmp(t,del) == 0) continue; /* test, whether to delete */ |
---|
1661 | if (sa[0] == 0) continue; |
---|
1662 | error = g_bs_add_value_tag_to_hash(gb_main,hash,t,sa,rtag,srt,aci,gbd); /* tag found, use tag */ |
---|
1663 | if (error) break; |
---|
1664 | } |
---|
1665 | s = se; |
---|
1666 | } |
---|
1667 | return error; |
---|
1668 | } |
---|
1669 | |
---|
1670 | static long g_bs_merge_tags(const char *tag, long val, void *cd_sub_result) { |
---|
1671 | struct GBS_strstruct *sub_result = (struct GBS_strstruct*)cd_sub_result; |
---|
1672 | |
---|
1673 | GBS_strcat(sub_result, tag); |
---|
1674 | GBS_strcat(sub_result, ","); |
---|
1675 | |
---|
1676 | return val; |
---|
1677 | } |
---|
1678 | |
---|
1679 | static long g_bs_read_tagged_hash(const char *value, long subhash, void *cd_g_bs_collect_tags_hash) { |
---|
1680 | char *str; |
---|
1681 | static int counter = 0; |
---|
1682 | struct GBS_strstruct *sub_result = GBS_stropen(100); |
---|
1683 | |
---|
1684 | GBS_hash_do_sorted_loop((GB_HASH *)subhash, g_bs_merge_tags, GBS_HCF_sortedByKey, sub_result); |
---|
1685 | GBS_intcat(sub_result, counter++); /* create a unique number */ |
---|
1686 | |
---|
1687 | str = GBS_strclose(sub_result); |
---|
1688 | |
---|
1689 | GB_HASH *g_bs_collect_tags_hash = (GB_HASH*)cd_g_bs_collect_tags_hash; |
---|
1690 | GBS_write_hash(g_bs_collect_tags_hash, str,(long)strdup(value)); /* send output to new hash for sorting */ |
---|
1691 | |
---|
1692 | free(str); |
---|
1693 | return 0; |
---|
1694 | } |
---|
1695 | |
---|
1696 | static long g_bs_read_final_hash(const char *tag, long value, void *cd_merge_result) { |
---|
1697 | struct GBS_strstruct *merge_result = (struct GBS_strstruct*)cd_merge_result; |
---|
1698 | |
---|
1699 | char *lk = strrchr(tag,','); |
---|
1700 | if (lk) { /* remove number at end */ |
---|
1701 | *lk = 0; |
---|
1702 | GBS_strcat(merge_result, " ["); |
---|
1703 | GBS_strcat(merge_result, tag); |
---|
1704 | GBS_strcat(merge_result, "] "); |
---|
1705 | } |
---|
1706 | GBS_strcat(merge_result,(char *)value); |
---|
1707 | return value; |
---|
1708 | } |
---|
1709 | |
---|
1710 | static char *g_bs_get_string_of_tag_hash(GB_HASH *tag_hash){ |
---|
1711 | struct GBS_strstruct *merge_result = GBS_stropen(256); |
---|
1712 | GB_HASH *collect_tags_hash = GBS_create_dynaval_hash(1024, GB_IGNORE_CASE, GBS_dynaval_free); |
---|
1713 | |
---|
1714 | GBS_hash_do_sorted_loop(tag_hash, g_bs_read_tagged_hash, GBS_HCF_sortedByKey, collect_tags_hash); /* move everything into collect_tags_hash */ |
---|
1715 | GBS_hash_do_sorted_loop(collect_tags_hash, g_bs_read_final_hash, GBS_HCF_sortedByKey, merge_result); |
---|
1716 | |
---|
1717 | GBS_free_hash(collect_tags_hash); |
---|
1718 | return GBS_strclose(merge_result); |
---|
1719 | } |
---|
1720 | |
---|
1721 | long g_bs_free_hash_of_hashes_elem(const char *key, long val, void *dummy) { |
---|
1722 | GB_HASH *hash = (GB_HASH*)val; |
---|
1723 | GBUSE(key); |
---|
1724 | GBUSE(dummy); |
---|
1725 | if (hash) GBS_free_hash(hash); |
---|
1726 | return 0; |
---|
1727 | } |
---|
1728 | static void g_bs_free_hash_of_hashes(GB_HASH *hash) { |
---|
1729 | GBS_hash_do_loop(hash, g_bs_free_hash_of_hashes_elem, NULL); |
---|
1730 | GBS_free_hash(hash); |
---|
1731 | } |
---|
1732 | |
---|
1733 | char *GBS_merge_tagged_strings(const char *s1, const char *tag1, const char *replace1, const char *s2, const char *tag2, const char *replace2){ |
---|
1734 | /* Create a tagged string from two tagged strings: |
---|
1735 | * a tagged string is somthing like '[tag,tag,tag] string [tag] string [tag,tag] string' |
---|
1736 | * |
---|
1737 | * if 's2' is not empty, then delete tag 'replace1' in 's1' |
---|
1738 | * if 's1' is not empty, then delete tag 'replace2' in 's2' |
---|
1739 | * |
---|
1740 | * if result is NULL, an error has been exported. |
---|
1741 | */ |
---|
1742 | |
---|
1743 | char *str1 = strdup(s1); |
---|
1744 | char *str2 = strdup(s2); |
---|
1745 | char *t1 = GBS_string_2_key(tag1); |
---|
1746 | char *t2 = GBS_string_2_key(tag2); |
---|
1747 | char *result = 0; |
---|
1748 | GB_ERROR error = 0; |
---|
1749 | GB_HASH *hash = GBS_create_hash(16, GB_MIND_CASE); |
---|
1750 | |
---|
1751 | if (!strlen(s1)) replace2 = 0; |
---|
1752 | if (!strlen(s2)) replace1 = 0; |
---|
1753 | |
---|
1754 | if (replace1 && replace1[0] == 0) replace1 = 0; |
---|
1755 | if (replace2 && replace2[0] == 0) replace2 = 0; |
---|
1756 | |
---|
1757 | error = g_bs_convert_string_to_tagged_hash(hash,str1,t1,replace1,0,0,0,0,0); |
---|
1758 | if (!error) error = g_bs_convert_string_to_tagged_hash(hash,str2,t2,replace2,0,0,0,0,0); |
---|
1759 | |
---|
1760 | if (!error) { |
---|
1761 | result = g_bs_get_string_of_tag_hash(hash); |
---|
1762 | } |
---|
1763 | else { |
---|
1764 | GB_export_error(error); |
---|
1765 | } |
---|
1766 | |
---|
1767 | g_bs_free_hash_of_hashes(hash); |
---|
1768 | |
---|
1769 | free(t2); |
---|
1770 | free(t1); |
---|
1771 | free(str2); |
---|
1772 | free(str1); |
---|
1773 | |
---|
1774 | return result; |
---|
1775 | } |
---|
1776 | |
---|
1777 | char *GBS_string_eval_tagged_string(GBDATA *gb_main, const char *s, const char *dt, const char *tag, const char *srt, const char *aci, GBDATA *gbd) { |
---|
1778 | /* if 's' is untagged, tag it with default tag 'dt'. |
---|
1779 | * if 'tag' is != NULL -> apply 'srt' or 'aci' to that part of the content of 's', which is tagged with 'tag' |
---|
1780 | * |
---|
1781 | * if result is NULL, an error has been exported. |
---|
1782 | */ |
---|
1783 | |
---|
1784 | char *str = strdup(s); |
---|
1785 | char *default_tag = GBS_string_2_key(dt); |
---|
1786 | GB_HASH *hash = GBS_create_hash(16, GB_MIND_CASE); |
---|
1787 | char *result = 0; |
---|
1788 | GB_ERROR error = g_bs_convert_string_to_tagged_hash(hash,str,default_tag,0,gb_main,tag,srt,aci,gbd); |
---|
1789 | |
---|
1790 | if (!error) { |
---|
1791 | result = g_bs_get_string_of_tag_hash(hash); |
---|
1792 | } |
---|
1793 | else { |
---|
1794 | GB_export_error(error); |
---|
1795 | } |
---|
1796 | |
---|
1797 | g_bs_free_hash_of_hashes(hash); |
---|
1798 | free(default_tag); |
---|
1799 | free(str); |
---|
1800 | |
---|
1801 | return result; |
---|
1802 | } |
---|
1803 | |
---|
1804 | |
---|
1805 | char *GB_read_as_tagged_string(GBDATA *gbd, const char *tagi){ |
---|
1806 | char *s; |
---|
1807 | char *tag; |
---|
1808 | char *buf; |
---|
1809 | char *se; /* string end */ |
---|
1810 | char *sa; /* string anfang and tag end */ |
---|
1811 | char *ts; /* tag start */ |
---|
1812 | char *t; |
---|
1813 | |
---|
1814 | buf = s = GB_read_as_string(gbd); |
---|
1815 | if (!s) return s; |
---|
1816 | if (!tagi) return s; |
---|
1817 | if (!strlen(tagi)) return s; |
---|
1818 | |
---|
1819 | tag = GBS_string_2_key(tagi); |
---|
1820 | |
---|
1821 | while(s){ |
---|
1822 | ts = strchr(s,'['); |
---|
1823 | if (!ts) goto notfound; /* no tag */ |
---|
1824 | |
---|
1825 | *(ts++) = 0; |
---|
1826 | |
---|
1827 | sa = strchr(ts,']'); |
---|
1828 | if (!sa) goto notfound; |
---|
1829 | |
---|
1830 | *sa++ = 0; |
---|
1831 | while (*sa == ' ') sa++; |
---|
1832 | |
---|
1833 | se = strchr(sa,'['); |
---|
1834 | if (se) { |
---|
1835 | while (se>sa && se[-1] == ' ') se--; |
---|
1836 | *(se++) = 0; |
---|
1837 | } |
---|
1838 | for (t = strtok(ts,","); t; t = strtok(0,",")){ |
---|
1839 | if (strcmp(t,tag) == 0) { |
---|
1840 | s = strdup(sa); |
---|
1841 | free(buf); |
---|
1842 | goto found; |
---|
1843 | } |
---|
1844 | } |
---|
1845 | s = se; |
---|
1846 | } |
---|
1847 | notfound: |
---|
1848 | /* Nothing found */ |
---|
1849 | free(buf); |
---|
1850 | s = 0; |
---|
1851 | found: |
---|
1852 | free(tag); |
---|
1853 | return s; |
---|
1854 | } |
---|
1855 | |
---|
1856 | |
---|
1857 | /* be CAREFUL : this function is used to save ARB ASCII database (i.e. properties) |
---|
1858 | * used as well to save perl macros |
---|
1859 | * |
---|
1860 | * when changing GBS_fwrite_string -> GBS_fread_string needs to be fixed as well |
---|
1861 | * |
---|
1862 | * always keep in mind, that many users have databases/macros written with older |
---|
1863 | * versions of this function. They MUST load proper!!! |
---|
1864 | */ |
---|
1865 | void GBS_fwrite_string(const char *strngi,FILE *out){ |
---|
1866 | unsigned char *strng = (unsigned char *)strngi; |
---|
1867 | int c; |
---|
1868 | |
---|
1869 | putc('"',out); |
---|
1870 | |
---|
1871 | while ( (c= *strng++) ) { |
---|
1872 | if (c < 32) { |
---|
1873 | putc('\\',out); |
---|
1874 | if (c == '\n') |
---|
1875 | putc('n',out); |
---|
1876 | else if (c == '\t') |
---|
1877 | putc('t',out); |
---|
1878 | else if ( c<25 ) { |
---|
1879 | putc(c+'@',out); /* characters ASCII 0..24 encoded as \@..\X (\n and \t are done above) */ |
---|
1880 | }else{ |
---|
1881 | putc(c+('0'-25),out);/* characters ASCII 25..31 encoded as \0..\6 */ |
---|
1882 | } |
---|
1883 | }else if (c == '"'){ |
---|
1884 | putc('\\',out); |
---|
1885 | putc('"',out); |
---|
1886 | }else if (c == '\\'){ |
---|
1887 | putc('\\',out); |
---|
1888 | putc('\\',out); |
---|
1889 | }else{ |
---|
1890 | putc(c,out); |
---|
1891 | } |
---|
1892 | } |
---|
1893 | putc('"',out); |
---|
1894 | } |
---|
1895 | |
---|
1896 | /* Read a string from a file written by GBS_fwrite_string, |
---|
1897 | * Searches first '"' |
---|
1898 | * |
---|
1899 | * WARNING : changing this function affects perl-macro execution (read warnings for GBS_fwrite_string) |
---|
1900 | * any changes should be done in GBS_fconvert_string too. |
---|
1901 | */ |
---|
1902 | |
---|
1903 | char *GBS_fread_string(FILE *in) { |
---|
1904 | void *strstr = GBS_stropen(1024); |
---|
1905 | int x; |
---|
1906 | |
---|
1907 | while ((x = getc(in)) != '"' ) if (x == EOF) break; /* Search first '"' */ |
---|
1908 | |
---|
1909 | if (x != EOF) { |
---|
1910 | while ((x = getc(in)) != '"' ){ |
---|
1911 | if (x == EOF) break; |
---|
1912 | if (x == '\\'){ |
---|
1913 | x = getc(in); if (x==EOF) break; |
---|
1914 | if (x == 'n') { |
---|
1915 | GBS_chrcat(strstr,'\n'); |
---|
1916 | continue; |
---|
1917 | } |
---|
1918 | if (x == 't') { |
---|
1919 | GBS_chrcat(strstr,'\t'); |
---|
1920 | continue; |
---|
1921 | } |
---|
1922 | if (x>='@' && x <='@'+ 25) { |
---|
1923 | GBS_chrcat(strstr,x-'@'); |
---|
1924 | continue; |
---|
1925 | } |
---|
1926 | if (x>='0' && x <='9') { |
---|
1927 | GBS_chrcat(strstr,x-('0'-25)); |
---|
1928 | continue; |
---|
1929 | } |
---|
1930 | /* all other backslashes are simply skipped */ |
---|
1931 | } |
---|
1932 | GBS_chrcat(strstr,x); |
---|
1933 | } |
---|
1934 | } |
---|
1935 | return GBS_strclose(strstr); |
---|
1936 | } |
---|
1937 | |
---|
1938 | /* does similiar decoding as GBS_fread_string but works directly on an existing buffer |
---|
1939 | * (WARNING : GBS_fconvert_string is used by gb_read_file which reads ARB ASCII databases!!) |
---|
1940 | * |
---|
1941 | * inserts \0 behind decoded string (removes the closing '"') |
---|
1942 | * returns a pointer behind the end (") of the _encoded_ string |
---|
1943 | * returns NULL if a 0-character is found |
---|
1944 | */ |
---|
1945 | char *GBS_fconvert_string(char *buffer) { |
---|
1946 | char *t = buffer; |
---|
1947 | char *f = buffer; |
---|
1948 | int x; |
---|
1949 | |
---|
1950 | ad_assert(f[-1] == '"'); |
---|
1951 | /* the opening " has already been read */ |
---|
1952 | |
---|
1953 | while ((x = *f++) != '"') { |
---|
1954 | if (!x) break; |
---|
1955 | |
---|
1956 | if (x == '\\') { |
---|
1957 | x = *f++; |
---|
1958 | if (!x) break; |
---|
1959 | |
---|
1960 | if (x == 'n') { |
---|
1961 | *t++ = '\n'; |
---|
1962 | continue; |
---|
1963 | } |
---|
1964 | if (x == 't') { |
---|
1965 | *t++ = '\t'; |
---|
1966 | continue; |
---|
1967 | } |
---|
1968 | if (x>='@' && x <='@'+ 25) { |
---|
1969 | *t++ = x-'@'; |
---|
1970 | continue; |
---|
1971 | } |
---|
1972 | if (x>='0' && x <='9') { |
---|
1973 | *t++ = x-('0'-25); |
---|
1974 | continue; |
---|
1975 | } |
---|
1976 | /* all other backslashes are simply skipped */ |
---|
1977 | } |
---|
1978 | *t++ = x; |
---|
1979 | } |
---|
1980 | |
---|
1981 | if (!x) return 0; // error (string should not contain 0-character) |
---|
1982 | ad_assert(x == '"'); |
---|
1983 | |
---|
1984 | t[0] = 0; |
---|
1985 | return f; |
---|
1986 | } |
---|
1987 | |
---|
1988 | char *GBS_replace_tabs_by_spaces(const char *text){ |
---|
1989 | int tlen = strlen(text); |
---|
1990 | void *mfile = GBS_stropen(tlen * 3/2); |
---|
1991 | int tabpos = 0; |
---|
1992 | int c; |
---|
1993 | while ((c=*(text++))) { |
---|
1994 | if (c == '\t'){ |
---|
1995 | int ntab = (tabpos + 8) & 0xfffff8; |
---|
1996 | while(tabpos < ntab){ |
---|
1997 | GBS_chrcat(mfile,' '); |
---|
1998 | tabpos++; |
---|
1999 | } |
---|
2000 | continue; |
---|
2001 | } |
---|
2002 | tabpos ++; |
---|
2003 | if (c== '\n'){ |
---|
2004 | tabpos = 0; |
---|
2005 | } |
---|
2006 | GBS_chrcat(mfile,c); |
---|
2007 | } |
---|
2008 | return GBS_strclose(mfile); |
---|
2009 | } |
---|
2010 | |
---|
2011 | int GBS_strscmp(const char *s1, const char *s2) { |
---|
2012 | int cmp = 0; |
---|
2013 | size_t idx = 0; |
---|
2014 | while (!cmp) { |
---|
2015 | if (!s1[idx] || !s2[idx]) break; |
---|
2016 | cmp = s1[idx] - s2[idx]; |
---|
2017 | ++idx; |
---|
2018 | } |
---|
2019 | return cmp; |
---|
2020 | } |
---|
2021 | |
---|
2022 | const char *GBS_readable_size(unsigned long long size) { |
---|
2023 | /* return human readable size information */ |
---|
2024 | /* returned string is maximal 7 characters long */ |
---|
2025 | |
---|
2026 | if (size<1000) return GBS_global_string("%llu b", size); |
---|
2027 | |
---|
2028 | const char *units = "kMGTPEZY"; // kilo, Mega, Giga, Tera, ... should be enough forever |
---|
2029 | int i; |
---|
2030 | |
---|
2031 | for (i = 0; units[i]; ++i) { |
---|
2032 | char unit = units[i]; |
---|
2033 | if (size<1000*1024) { |
---|
2034 | double amount = size/(double)1024; |
---|
2035 | if (amount<10.0) return GBS_global_string("%4.2f %cb", amount+0.005, unit); |
---|
2036 | if (amount<100.0) return GBS_global_string("%4.1f %cb", amount+0.05, unit); |
---|
2037 | return GBS_global_string("%i %cb", (int)(amount+0.5), unit); |
---|
2038 | } |
---|
2039 | size /= 1024; // next unit |
---|
2040 | } |
---|
2041 | ad_assert(0); |
---|
2042 | return "<much>"; |
---|
2043 | } |
---|
2044 | |
---|
2045 | char *GBS_trim(const char *str) { |
---|
2046 | // trim whitespace at beginning and end of 'str' |
---|
2047 | const char *whitespace = " \t\n"; |
---|
2048 | while (str[0] && strchr(whitespace, str[0])) str++; |
---|
2049 | |
---|
2050 | const char *end = strchr(str, 0)-1; |
---|
2051 | while (end >= str && strchr(whitespace, end[0])) end--; |
---|
2052 | |
---|
2053 | return GB_strpartdup(str, end); |
---|
2054 | } |
---|