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