Mercurial > ~mikael > mcabber > hg
comparison mcabber/mcabber/utils.c @ 1668:41c26b7d2890
Install mcabber headers
* Change mcabber headers naming scheme
* Move 'src/' -> 'mcabber/'
* Add missing include <mcabber/config.h>'s
* Create and install clean config.h version in 'include/'
* Move "dirty" config.h version to 'mcabber/'
* Add $(top_srcdir) to compiler include path
* Update modules HOWTO
author | Myhailo Danylenko <isbear@ukrpost.net> |
---|---|
date | Mon, 18 Jan 2010 15:36:19 +0200 |
parents | mcabber/src/utils.c@c3c7d6d0348f |
children | f02e7076ccec |
comparison
equal
deleted
inserted
replaced
1667:8af0e0ad20ad | 1668:41c26b7d2890 |
---|---|
1 /* | |
2 * utils.c -- Various utility functions | |
3 * | |
4 * Copyright (C) 2005-2009 Mikael Berthe <mikael@lilotux.net> | |
5 * Some of the ut_* functions are derived from Cabber debug/log code. | |
6 * from_iso8601() comes from the Pidgin (libpurple) project. | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or (at | |
11 * your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, but | |
14 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 * General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
21 * USA | |
22 */ | |
23 | |
24 #include <config.h> | |
25 | |
26 #include <stdio.h> | |
27 #include <stdlib.h> | |
28 #include <string.h> | |
29 #include <stdarg.h> | |
30 | |
31 #ifdef HAVE_LIBIDN | |
32 #include <idna.h> | |
33 #include <stringprep.h> | |
34 static char idnprep[1024]; | |
35 #endif | |
36 | |
37 #include <glib.h> | |
38 #include <glib/gprintf.h> | |
39 | |
40 /* For Cygwin (thanks go to Yitzchak Scott-Thoennes) */ | |
41 #ifdef __CYGWIN__ | |
42 # define timezonevar | |
43 extern long timezone; | |
44 #endif | |
45 #include <time.h> | |
46 #include <unistd.h> | |
47 #include <sys/types.h> | |
48 #include <sys/stat.h> | |
49 #include <ctype.h> | |
50 | |
51 #include "utils.h" | |
52 #include "logprint.h" | |
53 | |
54 static int DebugEnabled; | |
55 static char *FName; | |
56 | |
57 // jidtodisp(jid) | |
58 // Strips the resource part from the jid | |
59 // The caller should g_free the result after use. | |
60 char *jidtodisp(const char *fjid) | |
61 { | |
62 char *ptr; | |
63 char *alias; | |
64 | |
65 alias = g_strdup(fjid); | |
66 | |
67 if ((ptr = strchr(alias, JID_RESOURCE_SEPARATOR)) != NULL) { | |
68 *ptr = 0; | |
69 } | |
70 return alias; | |
71 } | |
72 | |
73 char *jid_get_username(const char *fjid) | |
74 { | |
75 char *ptr; | |
76 char *username; | |
77 | |
78 username = g_strdup(fjid); | |
79 if ((ptr = strchr(username, JID_DOMAIN_SEPARATOR)) != NULL) { | |
80 *ptr = 0; | |
81 } | |
82 return username; | |
83 } | |
84 | |
85 char *compose_jid(const char *username, const char *servername, | |
86 const char *resource) | |
87 { | |
88 char *fjid; | |
89 | |
90 if (!strchr(username, JID_DOMAIN_SEPARATOR)) { | |
91 fjid = g_strdup_printf("%s%c%s%c%s", username, | |
92 JID_DOMAIN_SEPARATOR, servername, | |
93 JID_RESOURCE_SEPARATOR, resource); | |
94 } else { | |
95 fjid = g_strdup_printf("%s%c%s", username, | |
96 JID_RESOURCE_SEPARATOR, resource); | |
97 } | |
98 return fjid; | |
99 } | |
100 | |
101 gboolean jid_equal(const char *jid1, const char *jid2) | |
102 { | |
103 char *a,*b; | |
104 int ret; | |
105 if (!jid1 && !jid2) | |
106 return TRUE; | |
107 if (!jid1 || !jid2) | |
108 return FALSE; | |
109 | |
110 a = jidtodisp(jid1); | |
111 b = jidtodisp(jid2); | |
112 ret = strcasecmp(a, b); | |
113 g_free(a); | |
114 g_free(b); | |
115 return (ret == 0) ? TRUE : FALSE; | |
116 } | |
117 | |
118 // expand_filename(filename) | |
119 // Expand "~/" with the $HOME env. variable in a file name. | |
120 // The caller must free the string after use. | |
121 char *expand_filename(const char *fname) | |
122 { | |
123 if (!fname) | |
124 return NULL; | |
125 if (!strncmp(fname, "~/", 2)) { | |
126 char *homedir = getenv("HOME"); | |
127 if (homedir) | |
128 return g_strdup_printf("%s%s", homedir, fname+1); | |
129 } | |
130 return g_strdup(fname); | |
131 } | |
132 | |
133 void fingerprint_to_hex(const unsigned char *fpr, char hex[49]) | |
134 { | |
135 int i; | |
136 char *p; | |
137 | |
138 for (p = hex, i = 0; i < 15; i++, p+=3) | |
139 g_sprintf(p, "%02X:", fpr[i]); | |
140 g_sprintf(p, "%02X", fpr[i]); | |
141 hex[48] = '\0'; | |
142 } | |
143 | |
144 gboolean hex_to_fingerprint(const char *hex, char fpr[16]) | |
145 { | |
146 int i; | |
147 char *p; | |
148 | |
149 if (strlen(hex) != 47) | |
150 return FALSE; | |
151 for (i = 0, p = (char*)hex; *p && *(p+1); i++, p += 3) | |
152 fpr[i] = (char) g_ascii_strtoull (p, NULL, 16); | |
153 return TRUE; | |
154 } | |
155 | |
156 void ut_InitDebug(int level, const char *filename) | |
157 { | |
158 FILE *fp; | |
159 struct stat buf; | |
160 int err; | |
161 | |
162 if (level < 1) { | |
163 DebugEnabled = 0; | |
164 FName = NULL; | |
165 return; | |
166 } | |
167 | |
168 if (filename) | |
169 FName = expand_filename(filename); | |
170 else { | |
171 FName = getenv("HOME"); | |
172 if (!FName) | |
173 FName = g_strdup("/tmp/mcabberlog"); | |
174 else { | |
175 FName = g_strdup_printf("%s/mcabberlog", FName); | |
176 } | |
177 } | |
178 | |
179 DebugEnabled = level; | |
180 | |
181 fp = fopen(FName, "a"); | |
182 if (!fp) { | |
183 fprintf(stderr, "ERROR: Cannot open tracelog file\n"); | |
184 return; | |
185 } | |
186 | |
187 err = fstat(fileno(fp), &buf); | |
188 if (err || buf.st_uid != geteuid()) { | |
189 fclose(fp); | |
190 DebugEnabled = 0; | |
191 FName = NULL; | |
192 if (err) { | |
193 fprintf(stderr, "ERROR: cannot stat the tracelog file!\n"); | |
194 } else { | |
195 fprintf(stderr, "ERROR: tracelog file does not belong to you!\n"); | |
196 } | |
197 return; | |
198 } | |
199 fchmod(fileno(fp), S_IRUSR|S_IWUSR); | |
200 | |
201 fprintf(fp, "New trace log started.\n----------------------\n"); | |
202 fclose(fp); | |
203 } | |
204 | |
205 void ut_WriteLog(unsigned int flag, const char *data) | |
206 { | |
207 if (!DebugEnabled || !FName) return; | |
208 | |
209 if (((DebugEnabled >= 2) && (flag & (LPRINT_LOG|LPRINT_DEBUG))) || | |
210 ((DebugEnabled == 1) && (flag & LPRINT_LOG))) { | |
211 FILE *fp = fopen(FName, "a+"); | |
212 if (!fp) { | |
213 scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot open tracelog file"); | |
214 return; | |
215 } | |
216 if (fputs(data, fp) == EOF) | |
217 scr_LogPrint(LPRINT_NORMAL, "ERROR: Cannot write to tracelog file"); | |
218 fclose(fp); | |
219 } | |
220 } | |
221 | |
222 // checkset_perm(name, setmode) | |
223 // Check the permissions of the "name" file/dir | |
224 // If setmode is true, correct the permissions if they are wrong | |
225 // Return values: -1 == bad file/dir, 0 == success, 1 == cannot correct | |
226 int checkset_perm(const char *name, unsigned int setmode) | |
227 { | |
228 int fd; | |
229 struct stat buf; | |
230 | |
231 #ifdef __CYGWIN__ | |
232 // Permission checking isn't efficient on Cygwin | |
233 return 0; | |
234 #endif | |
235 | |
236 fd = stat(name, &buf); | |
237 if (fd == -1) return -1; | |
238 | |
239 if (buf.st_uid != geteuid()) { | |
240 scr_LogPrint(LPRINT_LOGNORM, "Wrong file owner [%s]", name); | |
241 return 1; | |
242 } | |
243 | |
244 if (buf.st_mode & (S_IRGRP | S_IWGRP | S_IXGRP) || | |
245 buf.st_mode & (S_IROTH | S_IWOTH | S_IXOTH)) { | |
246 if (setmode) { | |
247 mode_t newmode = 0; | |
248 scr_LogPrint(LPRINT_LOGNORM, "Bad permissions [%s]", name); | |
249 if (S_ISDIR(buf.st_mode)) | |
250 newmode |= S_IXUSR; | |
251 newmode |= S_IRUSR | S_IWUSR; | |
252 if (chmod(name, newmode)) { | |
253 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Failed to correct permissions!"); | |
254 return 1; | |
255 } | |
256 scr_LogPrint(LPRINT_LOGNORM, "Permissions have been corrected"); | |
257 } else { | |
258 scr_LogPrint(LPRINT_LOGNORM, "WARNING: Bad permissions [%s]", name); | |
259 return 1; | |
260 } | |
261 } | |
262 | |
263 return 0; | |
264 } | |
265 | |
266 const char *ut_get_tmpdir(void) | |
267 { | |
268 static const char *tmpdir; | |
269 const char *tmpvars[] = { "MCABBERTMPDIR", "TMP", "TMPDIR", "TEMP" }; | |
270 unsigned int i; | |
271 | |
272 if (tmpdir) | |
273 return tmpdir; | |
274 | |
275 for (i = 0; i < (sizeof(tmpvars) / sizeof(const char *)); i++) { | |
276 tmpdir = getenv(tmpvars[i]); | |
277 if (tmpdir && tmpdir[0] && tmpdir[0] == '/' && tmpdir[1]) { | |
278 // Looks ok. | |
279 return tmpdir; | |
280 } | |
281 } | |
282 | |
283 // Default temporary directory | |
284 tmpdir = "/tmp"; | |
285 return tmpdir; | |
286 } | |
287 | |
288 // to_iso8601(dststr, timestamp) | |
289 // Convert timestamp to iso8601 format, and store it in dststr. | |
290 // NOTE: dststr should be at last 19 chars long. | |
291 // Return should be 0 | |
292 int to_iso8601(char *dststr, time_t timestamp) | |
293 { | |
294 struct tm *tm_time; | |
295 int ret; | |
296 | |
297 tm_time = gmtime(×tamp); | |
298 | |
299 ret = snprintf(dststr, 19, "%.4d%02d%02dT%02d:%02d:%02dZ", | |
300 (int)(1900+tm_time->tm_year), tm_time->tm_mon+1, tm_time->tm_mday, | |
301 tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec); | |
302 | |
303 return ((ret == -1) ? -1 : 0); | |
304 } | |
305 | |
306 // from_iso8601(timestamp, utc) | |
307 // This function came from the Pidgin project, gaim_str_to_time(). | |
308 // (Actually date may not be pure iso-8601) | |
309 // Thanks, guys! | |
310 // ** Modified by somian 10 Apr 2006 with advice from ysth. | |
311 time_t from_iso8601(const char *timestamp, int utc) | |
312 { | |
313 struct tm t; | |
314 time_t retval = 0; | |
315 char buf[32]; | |
316 char *c; | |
317 int tzoff = 0; | |
318 int hms_succ = 0; | |
319 int tmpyear; | |
320 | |
321 time(&retval); | |
322 localtime_r(&retval, &t); | |
323 | |
324 /* Reset time to midnight (00:00:00) */ | |
325 t.tm_hour = t.tm_min = t.tm_sec = 0; | |
326 | |
327 snprintf(buf, sizeof(buf), "%s", timestamp); | |
328 c = buf; | |
329 | |
330 /* 4 digit year */ | |
331 if (!sscanf(c, "%04d", &tmpyear)) return 0; | |
332 t.tm_year = tmpyear; | |
333 c+=4; | |
334 if (*c == '-') | |
335 c++; | |
336 | |
337 t.tm_year -= 1900; | |
338 | |
339 /* 2 digit month */ | |
340 if (!sscanf(c, "%02d", &t.tm_mon)) return 0; | |
341 c+=2; | |
342 if (*c == '-') | |
343 c++; | |
344 | |
345 t.tm_mon -= 1; | |
346 | |
347 /* 2 digit day */ | |
348 if (!sscanf(c, "%02d", &t.tm_mday)) return 0; | |
349 c+=2; | |
350 if (*c == 'T' || *c == '.') { /* we have more than a date, keep going */ | |
351 c++; /* skip the "T" */ | |
352 | |
353 /* 2 digit hour */ | |
354 if (sscanf(c, "%02d:%02d:%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3) | |
355 { | |
356 hms_succ = 1; | |
357 c += 8; | |
358 } | |
359 else if (sscanf(c, "%02d%02d%02d", &t.tm_hour, &t.tm_min, &t.tm_sec) == 3) | |
360 { | |
361 hms_succ = 1; | |
362 c += 6; | |
363 } | |
364 | |
365 if (hms_succ) { | |
366 int tzhrs, tzmins; | |
367 | |
368 if (*c == '.') /* dealing with precision we don't care about */ | |
369 c += 4; | |
370 | |
371 if ((*c == '+' || *c == '-') && | |
372 sscanf(c+1, "%02d:%02d", &tzhrs, &tzmins)) { | |
373 tzoff = tzhrs*60*60 + tzmins*60; | |
374 if (*c == '+') | |
375 tzoff *= -1; | |
376 } | |
377 | |
378 if (tzoff || utc) { | |
379 #ifdef HAVE_TM_GMTOFF | |
380 tzoff += t.tm_gmtoff; | |
381 #else | |
382 # ifdef HAVE_TIMEZONE | |
383 tzset(); /* making sure */ | |
384 tzoff -= timezone; | |
385 # endif | |
386 #endif | |
387 } | |
388 } | |
389 } | |
390 | |
391 t.tm_isdst = -1; | |
392 | |
393 retval = mktime(&t); | |
394 | |
395 retval += tzoff; | |
396 | |
397 return retval; | |
398 } | |
399 | |
400 /** | |
401 * Derived from libjabber/jid.c, because the libjabber version is not | |
402 * really convenient for our usage. | |
403 * | |
404 * Check if the full JID is valid | |
405 * Return 0 if it is valid, non zero otherwise | |
406 */ | |
407 int check_jid_syntax(const char *fjid) | |
408 { | |
409 const char *str; | |
410 const char *domain, *resource; | |
411 int domlen; | |
412 #ifdef HAVE_LIBIDN | |
413 char *idnpp; | |
414 int r; | |
415 #endif | |
416 | |
417 if (!fjid) return 1; | |
418 | |
419 domain = strchr(fjid, JID_DOMAIN_SEPARATOR); | |
420 | |
421 /* the username is optional */ | |
422 if (!domain) { | |
423 domain = fjid; | |
424 } else { | |
425 /* node identifiers may not be longer than 1023 bytes */ | |
426 if ((domain == fjid) || (domain-fjid > 1023)) | |
427 return 1; | |
428 domain++; | |
429 | |
430 #ifdef HAVE_LIBIDN | |
431 idnpp = idnprep; | |
432 str = fjid; | |
433 while (*str != JID_DOMAIN_SEPARATOR) | |
434 *idnpp++ = *str++; | |
435 *idnpp = 0; | |
436 | |
437 r = stringprep(idnprep, 1023, 0, stringprep_xmpp_nodeprep); | |
438 if (r != STRINGPREP_OK || !idnprep[0]) | |
439 return 1; | |
440 /* the username looks okay */ | |
441 #else | |
442 /* check for low and invalid ascii characters in the username */ | |
443 for (str = fjid; *str != JID_DOMAIN_SEPARATOR; str++) { | |
444 if (*str <= ' ' || *str == ':' || *str == JID_DOMAIN_SEPARATOR || | |
445 *str == '<' || *str == '>' || *str == '\'' || | |
446 *str == '"' || *str == '&') { | |
447 return 1; | |
448 } | |
449 } | |
450 /* the username is okay as far as we can tell without LIBIDN */ | |
451 #endif | |
452 } | |
453 | |
454 resource = strchr(domain, JID_RESOURCE_SEPARATOR); | |
455 | |
456 /* the resource is optional */ | |
457 if (resource) { | |
458 domlen = resource - domain; | |
459 resource++; | |
460 /* resources may not be longer than 1023 bytes */ | |
461 if ((*resource == '\0') || strlen(resource) > 1023) | |
462 return 1; | |
463 #ifdef HAVE_LIBIDN | |
464 strncpy(idnprep, resource, sizeof(idnprep)); | |
465 r = stringprep(idnprep, 1023, 0, stringprep_xmpp_resourceprep); | |
466 if (r != STRINGPREP_OK || !idnprep[0]) | |
467 return 1; | |
468 #endif | |
469 } else { | |
470 domlen = strlen(domain); | |
471 } | |
472 | |
473 /* there must be a domain identifier */ | |
474 if (domlen == 0) return 1; | |
475 | |
476 /* and it must not be longer than 1023 bytes */ | |
477 if (domlen > 1023) return 1; | |
478 | |
479 #ifdef HAVE_LIBIDN | |
480 idnpp = idnprep; | |
481 str = domain; | |
482 while (*str != '\0' && *str != JID_RESOURCE_SEPARATOR) | |
483 *idnpp++ = *str++; | |
484 *idnpp = 0; | |
485 | |
486 r = stringprep_nameprep(idnprep, 1023); | |
487 if (r != STRINGPREP_OK || !idnprep[0]) | |
488 return 1; | |
489 | |
490 if (idna_to_ascii_8z(idnprep, &idnpp, IDNA_USE_STD3_ASCII_RULES) != | |
491 IDNA_SUCCESS) | |
492 return 1; | |
493 else | |
494 free(idnpp); | |
495 #else | |
496 /* make sure the hostname is valid characters */ | |
497 for (str = domain; *str != '\0' && *str != JID_RESOURCE_SEPARATOR; str++) { | |
498 if (!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) | |
499 return 1; | |
500 } | |
501 #endif | |
502 | |
503 /* it's okay as far as we can tell */ | |
504 return 0; | |
505 } | |
506 | |
507 | |
508 inline void mc_strtolower(char *str) | |
509 { | |
510 if (!str) return; | |
511 for ( ; *str; str++) | |
512 *str = tolower(*str); | |
513 } | |
514 | |
515 // strip_arg_special_chars(string) | |
516 // Remove quotes and backslashes before an escaped quote | |
517 // Only quotes need a backslash | |
518 // Ex.: ["a b"] -> [a b]; [a\"b] -> [a"b] | |
519 void strip_arg_special_chars(char *s) | |
520 { | |
521 int instring = FALSE; | |
522 int escape = FALSE; | |
523 char *p; | |
524 | |
525 if (!s) return; | |
526 | |
527 for (p = s; *p; p++) { | |
528 if (*p == '"') { | |
529 if (!escape) { | |
530 instring = !instring; | |
531 strcpy(p, p+1); | |
532 p--; | |
533 } else | |
534 escape = FALSE; | |
535 } else if (*p == '\\') { | |
536 if (!escape) { | |
537 strcpy(p, p+1); | |
538 p--; | |
539 } | |
540 escape = !escape; | |
541 } else | |
542 escape = FALSE; | |
543 } | |
544 } | |
545 | |
546 // split_arg(arg, n, preservelast) | |
547 // Split the string arg into a maximum of n pieces, taking care of | |
548 // double quotes. | |
549 // Return a null-terminated array of strings. This array should be freed | |
550 // by the caller after use, for example with free_arg_lst(). | |
551 // If dontstriplast is true, the Nth argument isn't stripped (i.e. no | |
552 // processing of quote chars) | |
553 char **split_arg(const char *arg, unsigned int n, int dontstriplast) | |
554 { | |
555 char **arglst; | |
556 const char *p, *start, *end; | |
557 unsigned int i = 0; | |
558 int instring = FALSE; | |
559 int escape = FALSE; | |
560 | |
561 arglst = g_new0(char*, n+1); | |
562 | |
563 if (!arg || !n) return arglst; | |
564 | |
565 // Skip leading space | |
566 for (start = arg; *start && *start == ' '; start++) ; | |
567 // End of string pointer | |
568 for (end = start; *end; end++) ; | |
569 // Skip trailing space | |
570 while (end > start+1 && *(end-1) == ' ') | |
571 end--; | |
572 | |
573 for (p = start; p < end; p++) { | |
574 if (*p == '"' && !escape) | |
575 instring = !instring; | |
576 if (*p == '\\' && !escape) | |
577 escape = TRUE; | |
578 else if (escape) | |
579 escape = FALSE; | |
580 if (*p == ' ' && !instring && i+1 < n) { | |
581 // end of parameter | |
582 *(arglst+i) = g_strndup(start, p-start); | |
583 strip_arg_special_chars(*(arglst+i)); | |
584 for (start = p+1; *start && *start == ' '; start++) ; | |
585 p = start-1; | |
586 i++; | |
587 } | |
588 } | |
589 | |
590 if (start < end) { | |
591 *(arglst+i) = g_strndup(start, end-start); | |
592 if (!dontstriplast || i+1 < n) | |
593 strip_arg_special_chars(*(arglst+i)); | |
594 } | |
595 | |
596 return arglst; | |
597 } | |
598 | |
599 // free_arg_lst(arglst) | |
600 // Free an array allocated by split_arg() | |
601 void free_arg_lst(char **arglst) | |
602 { | |
603 char **arg_elt; | |
604 | |
605 for (arg_elt = arglst; *arg_elt; arg_elt++) | |
606 g_free(*arg_elt); | |
607 g_free(arglst); | |
608 } | |
609 | |
610 // replace_nl_with_dots(bufstr) | |
611 // Replace '\n' with "(...)" (or with a NUL if the string is too short) | |
612 void replace_nl_with_dots(char *bufstr) | |
613 { | |
614 char *p = strchr(bufstr, '\n'); | |
615 if (p) { | |
616 if (strlen(p) >= 5) | |
617 strcpy(p, "(...)"); | |
618 else | |
619 *p = 0; | |
620 } | |
621 } | |
622 | |
623 // ut_expand_tabs(text) | |
624 // Expand tabs and filter out some bad chars in string text. | |
625 // If there is no tab and no bad chars in the string, a pointer to text | |
626 // is returned (be careful _not_ to free the pointer in this case). | |
627 // If there are some tabs or bad chars, a new string with expanded chars | |
628 // and no bad chars is returned; this is up to the caller to free this | |
629 // string after use. | |
630 char *ut_expand_tabs(const char *text) | |
631 { | |
632 char *xtext, *linestart; | |
633 char *p, *q; | |
634 guint n = 0, bc = 0; | |
635 | |
636 xtext = (char*)text; | |
637 for (p=xtext; *p; p++) | |
638 if (*p == '\t') | |
639 n++; | |
640 else if (*p == '\x0d') | |
641 bc++; | |
642 // XXX Are there other special chars we should filter out? | |
643 | |
644 if (!n && !bc) | |
645 return (char*)text; | |
646 | |
647 xtext = g_new(char, strlen(text) + 1 + 8*n); | |
648 p = (char*)text; | |
649 q = linestart = xtext; | |
650 do { | |
651 if (*p == '\t') { | |
652 do { *q++ = ' '; } while ((q-linestart)%8); | |
653 } else if (*p != '\x0d') { | |
654 *q++ = *p; | |
655 if (*p =='\n') | |
656 linestart = q; | |
657 } | |
658 } while (*p++); | |
659 | |
660 return xtext; | |
661 } | |
662 | |
663 | |
664 /* Cygwin's newlib does not have strcasestr() */ | |
665 /* The author of the code before the endif is | |
666 * Jeffrey Stedfast <fejj@ximian.com> | |
667 * and this code is reusable in compliance with the GPL v2. -- somian */ | |
668 | |
669 #if !defined(HAVE_STRCASESTR) | |
670 | |
671 # define lowercase(c) (isupper ((int) (c)) ? tolower ((int) (c)) : (int) (c)) | |
672 # define bm_index(c, icase) ((icase) ? lowercase (c) : (int) (c)) | |
673 # define bm_equal(c1, c2, icase) ((icase) ? lowercase (c1) == lowercase (c2) : (c1) == (c2)) | |
674 | |
675 /* FIXME: this is just a guess... should really do some performace tests to get an accurate measure */ | |
676 # define bm_optimal(hlen, nlen) (((hlen) ? (hlen) > 20 : 1) && (nlen) > 10 ? 1 : 0) | |
677 | |
678 static unsigned char * | |
679 __boyer_moore (const unsigned char *haystack, size_t haystacklen, | |
680 const unsigned char *needle, size_t needlelen, int icase) | |
681 { | |
682 register unsigned char *hc_ptr, *nc_ptr; | |
683 unsigned char *he_ptr, *ne_ptr, *h_ptr; | |
684 size_t skiptable[256], n; | |
685 register int i; | |
686 | |
687 #ifdef BOYER_MOORE_CHECKS | |
688 /* we don't need to do these checks since memmem/strstr/etc do it already */ | |
689 /* if the haystack is shorter than the needle then we can't possibly match */ | |
690 if (haystacklen < needlelen) | |
691 return NULL; | |
692 | |
693 /* instant match if the pattern buffer is 0-length */ | |
694 if (needlelen == 0) | |
695 return (unsigned char *) haystack; | |
696 #endif /* BOYER_MOORE_CHECKS */ | |
697 | |
698 /* set a pointer at the end of each string */ | |
699 ne_ptr = (unsigned char *) needle + needlelen - 1; | |
700 he_ptr = (unsigned char *) haystack + haystacklen - 1; | |
701 | |
702 /* create our skip table */ | |
703 for (i = 0; i < 256; i++) | |
704 skiptable[i] = needlelen; | |
705 for (nc_ptr = (unsigned char *) needle; nc_ptr < ne_ptr; nc_ptr++) | |
706 skiptable[bm_index (*nc_ptr, icase)] = (size_t) (ne_ptr - nc_ptr); | |
707 | |
708 h_ptr = (unsigned char *) haystack; | |
709 while (haystacklen >= needlelen) { | |
710 hc_ptr = h_ptr + needlelen - 1; /* set the haystack compare pointer */ | |
711 nc_ptr = ne_ptr; /* set the needle compare pointer */ | |
712 | |
713 /* work our way backwards till they don't match */ | |
714 for (i = 0; nc_ptr > (unsigned char *) needle; nc_ptr--, hc_ptr--, i++) | |
715 if (!bm_equal (*nc_ptr, *hc_ptr, icase)) | |
716 break; | |
717 | |
718 if (!bm_equal (*nc_ptr, *hc_ptr, icase)) { | |
719 n = skiptable[bm_index (*hc_ptr, icase)]; | |
720 if (n == needlelen && i) | |
721 if (bm_equal (*ne_ptr, ((unsigned char *) needle)[0], icase)) | |
722 n--; | |
723 h_ptr += n; | |
724 haystacklen -= n; | |
725 } else | |
726 return (unsigned char *) h_ptr; | |
727 } | |
728 | |
729 return NULL; | |
730 } | |
731 | |
732 /* | |
733 * strcasestr: | |
734 * @haystack: string to search | |
735 * @needle: substring to search for | |
736 * | |
737 * Finds the first occurence of the substring @needle within the | |
738 * string @haystack ignoring case. | |
739 * | |
740 * Returns a pointer to the beginning of the substring match within | |
741 * @haystack, or NULL if the substring is not found. | |
742 **/ | |
743 char * | |
744 strcasestr (const char *haystack, const char *needle) | |
745 { | |
746 register unsigned char *h, *n, *hc, *nc; | |
747 size_t needlelen; | |
748 | |
749 needlelen = strlen (needle); | |
750 | |
751 if (needlelen == 0) { | |
752 return (char *) haystack; | |
753 } else if (bm_optimal (0, needlelen)) { | |
754 return (char *) __boyer_moore ((const unsigned char *) haystack, | |
755 strlen (haystack), | |
756 (const unsigned char *) needle, | |
757 needlelen, 1); | |
758 } | |
759 | |
760 h = (unsigned char *) haystack; | |
761 n = (unsigned char *) needle; | |
762 | |
763 while (*(h + needlelen - 1)) { | |
764 if (lowercase (*h) == lowercase (*n)) { | |
765 for (hc = h + 1, nc = n + 1; *hc && *nc; hc++, nc++) | |
766 if (lowercase (*hc) != lowercase (*nc)) | |
767 break; | |
768 | |
769 if (!*nc) | |
770 return (char *) h; | |
771 } | |
772 h++; | |
773 } | |
774 return NULL; | |
775 } | |
776 #endif /* !HAVE_STRCASESTR */ | |
777 | |
778 // startswith(str, word, ignore_case) | |
779 // Returns TRUE if string str starts with word. | |
780 int startswith(const char *str, const char *word, guint ignore_case) | |
781 { | |
782 if (ignore_case && !strncasecmp(str, word, strlen(word))) | |
783 return TRUE; | |
784 else if (!ignore_case && !strncmp(str, word, strlen(word))) | |
785 return TRUE; | |
786 return FALSE; | |
787 } | |
788 | |
789 /* vim: set expandtab cindent cinoptions=>2\:2(0: For Vim users... */ |