HUGE PATCH. This moves all of mime_parser.c and all
[citadel.git] / libcitadel / lib / tools.c
1 /*
2  *
3  */
4
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <errno.h>
15 #include <limits.h>
16
17 #if TIME_WITH_SYS_TIME
18 # include <sys/time.h>
19 # include <time.h>
20 #else
21 # if HAVE_SYS_TIME_H
22 #  include <sys/time.h>
23 # else
24 #  include <time.h>
25 # endif
26 #endif
27
28 #include "libcitadel.h"
29
30
31 #define TRUE  1
32 #define FALSE 0
33
34 typedef unsigned char byte;           /* Byte type */
35 static byte dtable[256] = "\0";       /* base64 decode table */
36 static byte etable[256] = "\0";       /* base64 encode table */
37
38 /* Month strings for date conversions */
39 char *ascmonths[12] = {
40         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
41         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
42 };
43
44 char *safestrncpy(char *dest, const char *src, size_t n)
45 {
46         int i = 0;
47
48         if (dest == NULL || src == NULL) {
49                 fprintf(stderr, "safestrncpy: NULL argument\n");
50                 abort();
51         }
52
53         do {
54                 dest[i] = src[i];
55                 if (dest[i] == 0) return(dest);
56                 ++i;
57         } while (i<n);
58         dest[n - 1] = 0;
59         return dest;
60 }
61
62
63
64 /*
65  * num_tokens()  -  discover number of parameters/tokens in a string
66  */
67 int num_tokens(const char *source, char tok)
68 {
69         int count = 1;
70         const char *ptr = source;
71
72         if (source == NULL) {
73                 return (0);
74         }
75
76         while (*ptr != '\0') {
77                 if (*ptr++ == tok) {
78                         ++count;
79                 }
80         }
81         
82         return (count);
83 }
84
85 //extern void cit_backtrace(void);
86
87
88 /*
89  * extract_token() - a string tokenizer
90  * returns -1 if not found, or length of token.
91  */
92 long extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
93 {
94         const char *s;                  //* source * /
95         int len = 0;                    //* running total length of extracted string * /
96         int current_token = 0;          //* token currently being processed * /
97
98         s = source;
99
100         if (dest == NULL) {
101                 return(-1);
102         }
103
104 //      cit_backtrace();
105 //      lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
106         dest[0] = 0;
107
108         if (s == NULL) {
109                 return(-1);
110         }
111         
112         maxlen--;
113
114         while (*s) {
115                 if (*s == separator) {
116                         ++current_token;
117                 }
118                 if ( (current_token == parmnum) && 
119                      (*s != separator) && 
120                      (len < maxlen) ) {
121                         dest[len] = *s;
122                         ++len;
123                 }
124                 else if ((current_token > parmnum) || (len >= maxlen)) {
125                         break;
126                 }
127                 ++s;
128         }
129
130         dest[len] = '\0';
131         if (current_token < parmnum) {
132 //              lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
133                 return(-1);
134         }
135 //      lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
136         return(len);
137 }
138 //*/
139
140
141 /*
142  * extract_token() - a string tokenizer
143  * /
144 long extract_token(char *dest, const char *source, int parmnum, char separator, int maxlen)
145 {
146         char *d;                // dest
147         const char *s;          // source
148         int count = 0;
149         int len = 0;
150
151         
152 //      cit_backtrace();
153         lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
154         strcpy(dest, "");
155
156         //  Locate desired parameter 
157         s = source;
158         while (count < parmnum) {
159                 //  End of string, bail!
160                 if (!*s) {
161                         s = NULL;
162                         break;
163                 }
164                 if (*s == separator) {
165                         count++;
166                 }
167                 s++;
168         }
169         if (!s) {
170                 lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
171                 return -1;              // Parameter not found
172         }
173         
174         for (d = dest; *s && *s != separator && ++len<maxlen; s++, d++) {
175                 *d = *s;
176         }
177         *d = 0;
178         lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
179         return 0;
180 }
181 */
182
183
184 /*
185  * remove_token() - a tokenizer that kills, maims, and destroys
186  */
187 void remove_token(char *source, int parmnum, char separator)
188 {
189         char *d, *s;            /* dest, source */
190         int count = 0;
191
192         /* Find desired parameter */
193         d = source;
194         while (count < parmnum) {
195                 /* End of string, bail! */
196                 if (!*d) {
197                         d = NULL;
198                         break;
199                 }
200                 if (*d == separator) {
201                         count++;
202                 }
203                 d++;
204         }
205         if (!d) return;         /* Parameter not found */
206
207         /* Find next parameter */
208         s = d;
209         while (*s && *s != separator) {
210                 s++;
211         }
212
213         /* Hack and slash */
214         if (*s)
215                 strcpy(d, ++s);
216         else if (d == source)
217                 *d = 0;
218         else
219                 *--d = 0;
220         /*
221         while (*s) {
222                 *d++ = *s++;
223         }
224         *d = 0;
225         */
226 }
227
228
229 /*
230  * extract_int()  -  extract an int parm w/o supplying a buffer
231  */
232 int extract_int(const char *source, int parmnum)
233 {
234         char buf[32];
235         
236         extract_token(buf, source, parmnum, '|', sizeof buf);
237         return(atoi(buf));
238 }
239
240 /*
241  * extract_long()  -  extract an long parm w/o supplying a buffer
242  */
243 long extract_long(const char *source, int parmnum)
244 {
245         char buf[32];
246         
247         extract_token(buf, source, parmnum, '|', sizeof buf);
248         return(atol(buf));
249 }
250
251
252 /*
253  * extract_unsigned_long() - extract an unsigned long parm
254  */
255 unsigned long extract_unsigned_long(const char *source, int parmnum)
256 {
257         char buf[32];
258
259         extract_token(buf, source, parmnum, '|', sizeof buf);
260         return strtoul(buf, NULL, 10);
261 }
262
263
264
265 void CtdlInitBase64Table(void)
266 {
267         int i;
268         /*      Fill dtable with character encodings.  */
269         
270         /* Encoder Table */
271         for (i = 0; i < 26; i++) {
272                 etable[i] = 'A' + i;
273                 etable[26 + i] = 'a' + i;
274         }
275         for (i = 0; i < 10; i++) {
276                 etable[52 + i] = '0' + i;
277         }
278         etable[62] = '+';
279         etable[63] = '/';
280         
281         /* Decoder Table */
282         for (i = 0; i < 255; i++) {
283                 dtable[i] = 0x80;
284         }
285         for (i = 'A'; i <= 'Z'; i++) {
286                 dtable[i] = 0 + (i - 'A');
287         }
288         for (i = 'a'; i <= 'z'; i++) {
289                 dtable[i] = 26 + (i - 'a');
290         }
291         for (i = '0'; i <= '9'; i++) {
292                 dtable[i] = 52 + (i - '0');
293         }
294         dtable['+'] = 62;
295         dtable['/'] = 63;
296         dtable['='] = 0;
297 }
298
299
300 /*
301  * CtdlDecodeBase64() and CtdlEncodeBase64() are adaptations of code by John Walker.
302  */
303
304 size_t CtdlEncodeBase64(char *dest, const char *source, size_t sourcelen, int linebreaks)
305 {
306         int i, hiteof = FALSE;
307         int spos = 0;
308         int dpos = 0;
309         int thisline = 0;
310
311         /**  Fill dtable with character encodings.  */
312
313         for (i = 0; i < 26; i++) {
314                 dtable[i] = 'A' + i;
315                 dtable[26 + i] = 'a' + i;
316         }
317         for (i = 0; i < 10; i++) {
318                 dtable[52 + i] = '0' + i;
319         }
320         dtable[62] = '+';
321         dtable[63] = '/';
322
323         while (!hiteof) {
324                 byte igroup[3], ogroup[4];
325                 int c, n;
326
327                 igroup[0] = igroup[1] = igroup[2] = 0;
328                 for (n = 0; n < 3; n++) {
329                         if (spos >= sourcelen) {
330                                 hiteof = TRUE;
331                                 break;
332                         }
333                         c = source[spos++];
334                         igroup[n] = (byte) c;
335                 }
336                 if (n > 0) {
337                         ogroup[0] = dtable[igroup[0] >> 2];
338                         ogroup[1] =
339                             dtable[((igroup[0] & 3) << 4) |
340                                    (igroup[1] >> 4)];
341                         ogroup[2] =
342                             dtable[((igroup[1] & 0xF) << 2) |
343                                    (igroup[2] >> 6)];
344                         ogroup[3] = dtable[igroup[2] & 0x3F];
345
346                         /**
347                          * Replace characters in output stream with "=" pad
348                          * characters if fewer than three characters were
349                          * read from the end of the input stream. 
350                          */
351
352                         if (n < 3) {
353                                 ogroup[3] = '=';
354                                 if (n < 2) {
355                                         ogroup[2] = '=';
356                                 }
357                         }
358                         for (i = 0; i < 4; i++) {
359                                 dest[dpos++] = ogroup[i];
360                                 dest[dpos] = 0;
361                         }
362                         thisline += 4;
363                         if ( (linebreaks) && (thisline > 70) ) {
364                                 dest[dpos++] = '\r';
365                                 dest[dpos++] = '\n';
366                                 dest[dpos] = 0;
367                                 thisline = 0;
368                         }
369                 }
370         }
371         if ( (linebreaks) && (thisline > 70) ) {
372                 dest[dpos++] = '\r';
373                 dest[dpos++] = '\n';
374                 dest[dpos] = 0;
375                 thisline = 0;
376         }
377
378         return(dpos);
379 }
380
381
382
383 /* 
384  * Convert base64-encoded to binary.  Returns the length of the decoded data.
385  * It will stop after reading 'length' bytes.
386  */
387 int CtdlDecodeBase64(char *dest, const char *source, size_t length)
388 {
389     int i, c;
390     int dpos = 0;
391     int spos = 0;
392
393
394     /*CONSTANTCONDITION*/
395     while (TRUE) {
396         byte a[4], b[4], o[3];
397
398         for (i = 0; i < 4; i++) {
399             if (spos >= length) {
400                 return(dpos);
401             }
402             c = source[spos++];
403
404             if (c == 0) {
405                 if (i > 0) {
406                     return(dpos);
407                 }
408                 return(dpos);
409             }
410             if (dtable[c] & 0x80) {
411                 /* Ignoring errors: discard invalid character. */
412                 i--;
413                 continue;
414             }
415             a[i] = (byte) c;
416             b[i] = (byte) dtable[c];
417         }
418         o[0] = (b[0] << 2) | (b[1] >> 4);
419         o[1] = (b[1] << 4) | (b[2] >> 2);
420         o[2] = (b[2] << 6) | b[3];
421         i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
422         if (i>=1) dest[dpos++] = o[0];
423         if (i>=2) dest[dpos++] = o[1];
424         if (i>=3) dest[dpos++] = o[2];
425         dest[dpos] = 0;
426         if (i < 3) {
427             return(dpos);
428         }
429     }
430 }
431
432
433 /*
434  * if we send out non ascii subjects, we encode it this way.
435  */
436 char *rfc2047encode(char *line, long length)
437 {
438         char *AlreadyEncoded;
439         char *result;
440         long end;
441 #define UTF8_HEADER "=?UTF-8?B?"
442
443         /* check if we're already done */
444         AlreadyEncoded = strstr(line, "=?");
445         if ((AlreadyEncoded != NULL) &&
446             ((strstr(AlreadyEncoded, "?B?") != NULL)||
447              (strstr(AlreadyEncoded, "?Q?") != NULL)))
448         {
449                 return strdup(line);
450         }
451
452         result = (char*) malloc(strlen(UTF8_HEADER) + 4 + length * 2);
453         strncpy (result, UTF8_HEADER, strlen (UTF8_HEADER));
454         CtdlEncodeBase64(result + strlen(UTF8_HEADER), line, length, 0);
455         end = strlen (result);
456         result[end]='?';
457         result[end+1]='=';
458         result[end+2]='\0';
459         return result;
460 }
461
462
463 /*
464  * Strip leading and trailing spaces from a string
465  */
466 void striplt(char *buf)
467 {
468         size_t len;
469         int a;
470
471         if (buf==NULL) return;
472         if (IsEmptyStr(buf)) return;
473         len = strlen(buf);
474         while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1])))
475                 buf[--len] = 0;
476         if (IsEmptyStr(buf)) return;
477         a = 0;
478         while ((!IsEmptyStr(buf)) && (isspace(buf[a])))
479                 a++;
480         if (a > 0)
481                 memmove(buf, &buf[a], len - a + 1);
482 }
483
484
485
486
487
488 /**
489  * \brief check for the presence of a character within a string (returns count)
490  * \param st the string to examine
491  * \param ch the char to search
492  * \return the position inside of st
493  */
494 int haschar(const char *st,int ch)
495 {
496         const char *ptr;
497         int b;
498         b = 0;
499         ptr = st;
500         while (!IsEmptyStr(ptr))
501         {
502                 if (*ptr == ch)
503                         ++b;
504                 ptr ++;
505         }
506         return (b);
507 }
508
509
510
511
512
513 /*
514  * Format a date/time stamp for output 
515  * seconds is whether to print the seconds
516  */
517 void fmt_date(char *buf, size_t n, time_t thetime, int seconds) {
518         struct tm tm;
519         int hour;
520
521         strcpy(buf, "");
522         localtime_r(&thetime, &tm);
523
524         hour = tm.tm_hour;
525         if (hour == 0)  hour = 12;
526         else if (hour > 12) hour = hour - 12;
527
528         if (seconds) {
529                 snprintf(buf, n, "%s %d %4d %d:%02d:%02d%s",
530                         ascmonths[tm.tm_mon],
531                         tm.tm_mday,
532                         tm.tm_year + 1900,
533                         hour,
534                         tm.tm_min,
535                         tm.tm_sec,
536                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
537                 );
538         } else {
539                 snprintf(buf, n, "%s %d %4d %d:%02d%s",
540                         ascmonths[tm.tm_mon],
541                         tm.tm_mday,
542                         tm.tm_year + 1900,
543                         hour,
544                         tm.tm_min,
545                         ( (tm.tm_hour >= 12) ? "pm" : "am" )
546                 );
547         }
548 }
549
550
551
552 /*
553  * Determine whether the specified message number is contained within the
554  * specified sequence set.
555  */
556 int is_msg_in_sequence_set(char *mset, long msgnum) {
557         int num_sets;
558         int s;
559         char setstr[128], lostr[128], histr[128];
560         long lo, hi;
561
562         num_sets = num_tokens(mset, ',');
563         for (s=0; s<num_sets; ++s) {
564                 extract_token(setstr, mset, s, ',', sizeof setstr);
565
566                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
567                 if (num_tokens(setstr, ':') >= 2) {
568                         extract_token(histr, setstr, 1, ':', sizeof histr);
569                         if (!strcmp(histr, "*")) {
570                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
571                         }
572                 } 
573                 else {
574                         strcpy(histr, lostr);
575                 }
576                 lo = atol(lostr);
577                 hi = atol(histr);
578
579                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
580         }
581
582         return(0);
583 }
584
585 /** 
586  * \brief Utility function to "readline" from memory
587  * \param start Location in memory from which we are reading.
588  * \param buf the buffer to place the string in.
589  * \param maxlen Size of string buffer
590  * \return Pointer to the source memory right after we stopped reading.
591  */
592 char *memreadline(char *start, char *buf, int maxlen)
593 {
594         char ch;
595         char *ptr;
596         int len = 0;            /**< tally our own length to avoid strlen() delays */
597
598         ptr = start;
599
600         while (1) {
601                 ch = *ptr++;
602                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
603                         buf[len++] = ch;
604                 }
605                 if ((ch == 10) || (ch == 0)) {
606                         buf[len] = 0;
607                         return ptr;
608                 }
609         }
610 }
611
612
613 /** 
614  * \brief Utility function to "readline" from memory
615  * \param start Location in memory from which we are reading.
616  * \param buf the buffer to place the string in.
617  * \param maxlen Size of string buffer
618  * \param retlen the length of the returned string
619  * \return Pointer to the source memory right after we stopped reading.
620  */
621 char *memreadlinelen(char *start, char *buf, int maxlen, int *retlen)
622 {
623         char ch;
624         char *ptr;
625         int len = 0;            /**< tally our own length to avoid strlen() delays */
626
627         ptr = start;
628
629         while (1) {
630                 ch = *ptr++;
631                 if ((len + 1 < (maxlen)) && (ch != 13) && (ch != 10)) {
632                         buf[len++] = ch;
633                 }
634                 if ((ch == 10) || (ch == 0)) {
635                         buf[len] = 0;
636                         *retlen = len;
637                         return ptr;
638                 }
639         }
640 }
641
642
643
644
645 /*
646  * Strip a boundarized substring out of a string (for example, remove
647  * parentheses and anything inside them).
648  */
649 void stripout(char *str, char leftboundary, char rightboundary) {
650         int a;
651         int lb = (-1);
652         int rb = (-1);
653
654         for (a = 0; a < strlen(str); ++a) {
655                 if (str[a] == leftboundary) lb = a;
656                 if (str[a] == rightboundary) rb = a;
657         }
658
659         if ( (lb > 0) && (rb > lb) ) {
660                 strcpy(&str[lb - 1], &str[rb + 1]);
661         }
662
663         else if ( (lb == 0) && (rb > lb) ) {
664                 strcpy(str, &str[rb + 1]);
665         }
666
667 }
668
669
670 /*
671  * Reduce a string down to a boundarized substring (for example, remove
672  * parentheses and anything outside them).
673  */
674 void stripallbut(char *str, char leftboundary, char rightboundary) {
675         int a;
676
677         for (a = 0; a < strlen(str); ++ a) {
678                 if (str[a] == leftboundary) strcpy(str, &str[a+1]);
679         }
680
681         for (a = 0; a < strlen(str); ++ a) {
682                 if (str[a] == rightboundary) str[a] = 0;
683         }
684
685 }
686
687 char *myfgets(char *s, int size, FILE *stream) {
688         char *ret = fgets(s, size, stream);
689         char *nl;
690
691         if (ret != NULL) {
692                 nl = strchr(s, '\n');
693
694                 if (nl != NULL)
695                         *nl = 0;
696         }
697
698         return ret;
699 }
700
701 /*
702  * Escape a string for feeding out as a URL.
703  * Output buffer must be big enough to handle escape expansion!
704  */
705 void urlesc(char *outbuf, char *strbuf)
706 {
707         int a, b, c;
708         char *ec = " #&;`'|*?-~<>^()[]{}$\\";
709
710         strcpy(outbuf, "");
711
712         for (a = 0; a < (int)strlen(strbuf); ++a) {
713                 c = 0;
714                 for (b = 0; b < strlen(ec); ++b) {
715                         if (strbuf[a] == ec[b])
716                                 c = 1;
717                 }
718                 b = strlen(outbuf);
719                 if (c == 1)
720                         sprintf(&outbuf[b], "%%%02x", strbuf[a]);
721                 else
722                         sprintf(&outbuf[b], "%c", strbuf[a]);
723         }
724 }
725
726
727
728 /*
729  * In our world, we want strcpy() to be able to work with overlapping strings.
730  */
731 #ifdef strcpy
732 #undef strcpy
733 #endif
734 char *strcpy(char *dest, const char *src) {
735         memmove(dest, src, (strlen(src) + 1) );
736         return(dest);
737 }
738
739
740 /*
741  * Generate a new, globally unique UID parameter for a calendar etc. object
742  */
743 void generate_uuid(char *buf) {
744         static int seq = 0;
745
746         sprintf(buf, "%lx-%lx-%x",
747                 time(NULL),
748                 (long)getpid(),
749                 (seq++)
750         );
751 }
752
753 /*
754  * bmstrcasestr() -- case-insensitive substring search
755  *
756  * This uses the Boyer-Moore search algorithm and is therefore quite fast.
757  * The code is roughly based on the strstr() replacement from 'tin' written
758  * by Urs Jannsen.
759  */
760 char *bmstrcasestr(char *text, char *pattern) {
761
762         register unsigned char *p, *t;
763         register int i, j, *delta;
764         register size_t p1;
765         int deltaspace[256];
766         size_t textlen;
767         size_t patlen;
768
769         textlen = strlen (text);
770         patlen = strlen (pattern);
771
772         /* algorithm fails if pattern is empty */
773         if ((p1 = patlen) == 0)
774                 return (text);
775
776         /* code below fails (whenever i is unsigned) if pattern too long */
777         if (p1 > textlen)
778                 return (NULL);
779
780         /* set up deltas */
781         delta = deltaspace;
782         for (i = 0; i <= 255; i++)
783                 delta[i] = p1;
784         for (p = (unsigned char *) pattern, i = p1; --i > 0;)
785                 delta[tolower(*p++)] = i;
786
787         /*
788          * From now on, we want patlen - 1.
789          * In the loop below, p points to the end of the pattern,
790          * t points to the end of the text to be tested against the
791          * pattern, and i counts the amount of text remaining, not
792          * including the part to be tested.
793          */
794         p1--;
795         p = (unsigned char *) pattern + p1;
796         t = (unsigned char *) text + p1;
797         i = textlen - patlen;
798         while(1) {
799                 if (tolower(p[0]) == tolower(t[0])) {
800                         if (strncasecmp ((const char *)(p - p1), (const char *)(t - p1), p1) == 0) {
801                                 return ((char *)t - p1);
802                         }
803                 }
804                 j = delta[tolower(t[0])];
805                 if (i < j)
806                         break;
807                 i -= j;
808                 t += j;
809         }
810         return (NULL);
811 }
812
813
814
815 /*
816  * Local replacement for controversial C library function that generates
817  * names for temporary files.  Included to shut up compiler warnings.
818  */
819 void CtdlMakeTempFileName(char *name, int len) {
820         int i = 0;
821
822         while (i++, i < 100) {
823                 snprintf(name, len, "/tmp/ctdl.%4lx.%04x",
824                         (long)getpid(),
825                         rand()
826                 );
827                 if (!access(name, F_OK)) {
828                         return;
829                 }
830         }
831 }
832
833
834
835 /*
836  * Determine whether the specified message number is contained within the specified set.
837  * Returns nonzero if the specified message number is in the specified message set string.
838  */
839 int is_msg_in_mset(char *mset, long msgnum) {
840         int num_sets;
841         int s;
842         char setstr[SIZ], lostr[SIZ], histr[SIZ];       /* was 1024 */
843         long lo, hi;
844
845         /*
846          * Now set it for all specified messages.
847          */
848         num_sets = num_tokens(mset, ',');
849         for (s=0; s<num_sets; ++s) {
850                 extract_token(setstr, mset, s, ',', sizeof setstr);
851
852                 extract_token(lostr, setstr, 0, ':', sizeof lostr);
853                 if (num_tokens(setstr, ':') >= 2) {
854                         extract_token(histr, setstr, 1, ':', sizeof histr);
855                         if (!strcmp(histr, "*")) {
856                                 snprintf(histr, sizeof histr, "%ld", LONG_MAX);
857                         }
858                 }
859                 else {
860                         strcpy(histr, lostr);
861                 }
862                 lo = atol(lostr);
863                 hi = atol(histr);
864
865                 if ((msgnum >= lo) && (msgnum <= hi)) return(1);
866         }
867
868         return(0);
869 }
870
871
872 /**
873  * \brief searches for a  paternn within asearch string
874  * \param search the string to search 
875  * \param patn the pattern to find in string
876  * \returns position in string
877  */
878 int pattern2(char *search, char *patn)
879 {
880         int a;
881         int len, plen;
882         len = strlen (search);
883         plen = strlen (patn);
884         for (a = 0; a < len; ++a) {
885                 if (!strncasecmp(&search[a], patn, plen))
886                         return (a);
887         }
888         return (-1);
889 }
890
891
892 /**
893  * \brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
894  * \param buf the string to modify
895  * \param len length of the string. 
896  */
897 void stripltlen(char *buf, int *len)
898 {
899         int delta = 0;
900         if (*len == 0) return;
901         while ((*len > delta) && (isspace(buf[delta]))){
902                 delta ++;
903         }
904         memmove (buf, &buf[delta], *len - delta + 1);
905         (*len) -=delta;
906
907         if (*len == 0) return;
908         while (isspace(buf[(*len) - 1])){
909                 buf[--(*len)] = '\0';
910         }
911 }
912