1 // crm_str_funcs.c - string handling functions
2
3 // Copyright 2004 Fidelis Assis
4 // Copyright 2001-2009 William S. Yerazunis.
5 // This file is under GPLv3, as described in COPYING.
6
7 // include some standard files
8 #include "crm114_sysincludes.h"
9
10 // include any local crm114 configuration file
11 #include "crm114_config.h"
12
13 // include the crm114 data structures file
14 #include "crm114_structs.h"
15
16 // and include the routine declarations file
17 #include "crm114.h"
18
19 int dontcare;
20 long internal_trace;
21 long user_trace;
22
23 // strnhash - generate the hash of a string of length N
24 // goals - fast, works well with short vars includng
25 // letter pairs and palindromes, not crypto strong, generates
26 // hashes that tend toward relative primality against common
27 // hash table lengths (so taking the output of this function
28 // modulo the hash table length gives a relatively uniform distribution
29 //
30 // In timing tests, this hash function can hash over 10 megabytes
31 // per second (using as text the full 2.4.9 linux kernel source)
32 // hashing individual whitespace-delimited tokens, on a Transmeta
33 // 666 MHz.
34
35 // This is a more portable hash function, compatible with the original.
36 // It should return the same value both on 32 and 64 bit architectures.
37 // The return type was changed to unsigned long hashes, and the other
38 // parts of the code updated accordingly.
39 // -- Fidelis
40 //
41 //
42 // unsigned long -> unsigned int, following Bill's idea that int is
43 // likely to be 32 bits.
44 // int32_t -> uint32_t, to get logical >> instead of arithmetic,
45 // and an and and in case some compiler takes the loophole that allows
46 // it not to implement logical right shift on processors that don't
47 // have that instruction.
48 // -- Kurt Hackenberg
49
strnhash(char * str,long len)50 unsigned int strnhash (char *str, long len)
51 {
52 long i;
53 uint32_t hval;
54 unsigned int tmp;
55
56 // initialize hval
57 hval = len;
58
59 // for each character in the incoming text:
60 for ( i = 0; i < len; i++)
61 {
62 // xor in the current byte against each byte of hval
63 // (which alone guarantees that every bit of input will have
64 // an effect on the output)
65
66 tmp = str[i] & 0xFF;
67 tmp = tmp | (tmp << 8) | (tmp << 16) | (tmp << 24);
68 hval ^= tmp;
69
70 // add some bits out of the middle as low order bits.
71 hval = hval + (( hval >> 12) & 0x0000ffff) ;
72
73 // swap most and min significative bytes
74 tmp = (hval << 24) | ((hval >> 24) & 0xff);
75 hval &= 0x00ffff00; // zero most and min significative bytes of hval
76 hval |= tmp; // OR with swapped bytes
77
78 // rotate hval 3 bits to the left (thereby making the
79 // 3rd msb of the above mess the msb of the output hash)
80 hval = (hval << 3) | ((hval >> 29) & 0x7);
81 }
82 return (hval);
83 }
84
85 ////////////////////////////////////////////////////////////////////////////
86 //
87 // Cached mmap stuff. Adapted from Win32 compatibility code from
88 // Barry Jaspan. Altered to not reveal the difference between a
89 // mapped file pointer and one of Barry's 'map' structs. In this
90 // code (unlike Barry's patches), all that is ever seen are
91 // pointers to memory (i.e. crm_mmap and crm_munmap have the same
92 // API and semantics as with the libc mmap() and munmap() calls),
93 // no structs are ever seen by the callers of this code.
94 //
95 // Bugs in the POSIX code are my fault. Bugs in the CRM_WINDOWS code are
96 // either mine or his. So there.
97 //
98
99 ///////////////////////////////////////////////////////////////////////////
100 //
101 // This code section (from this line to the line below that states
102 // that it is the end of the dual-licensed code section) is
103 // copyright and owned by William S. Yerazunis. In return for
104 // addition of significant derivative work, Barry Jaspan is hereby
105 // granted a full unlimited license to use this code section,
106 // including license to relicense under other licenses.
107 //
108 ////////////////////////////////////////////////////////////////////////////
109
110
111 // An mmap cell. This is how we cache.
112 //
113 typedef struct prototype_crm_mmap_cell
114 {
115 char *name;
116 long start;
117 long requested_len;
118 long actual_len;
119 time_t modification_time; // st_mtime - time last modified
120 void *addr;
121 long prot; // prot flags to be used, in the mmap() form
122 // that is, PROT_*, rather than O_*
123
124 long mode; // Mode is things like MAP_SHARED or MAP_LOCKED
125
126 int unmap_count; // counter - unmap this after UNMAP_COUNT_MAX
127 struct prototype_crm_mmap_cell *next, *prev;
128 #ifndef CRM_WINDOWS
129 int fd;
130 #else // CRM_WINDOWS
131 HANDLE fd, mapping;
132 #endif // CRM_WINDOWS
133 } CRM_MMAP_CELL;
134
135
136 // We want these to hang around but not be visible outside this file.
137
138 static CRM_MMAP_CELL *cache = NULL; // "volatile" for W32 compile bug
139
140
141 //////////////////////////////////////
142 //
143 // Force an unmap (don't look at the unmap_count, just do it)
144 // Watch out tho- this takes a CRM_MMAP_CELL, not a *ptr, so don't
145 // call it from anywhere except inside this file.
146 //
crm_unmap_file_internal(CRM_MMAP_CELL * map)147 static void crm_unmap_file_internal ( CRM_MMAP_CELL *map)
148 {
149 long munmap_status;
150
151 #ifndef CRM_WINDOWS
152 if (map->prot & PROT_WRITE)
153 msync (map->addr, map->actual_len, MS_ASYNC | MS_INVALIDATE);
154 munmap_status = munmap (map->addr, map->actual_len);
155 // fprintf (stderr, "Munmap_status is %ld\n", munmap_status);
156
157 // Because mmap/munmap doesn't set atime, nor set the "modified"
158 // flag, some network filesystems will fail to mark the file as
159 // modified and so their cacheing will make a mistake.
160 //
161 // The fix is that for files that were mmapped writably, to do
162 // a trivial read/write on the mapped file, to force the
163 // filesystem to repropagate it's caches.
164 //
165 if (map->prot & PROT_WRITE)
166 {
167 FEATURE_HEADER_STRUCT foo;
168 lseek (map->fd, 0, SEEK_SET);
169 dontcare = read (map->fd, &foo, sizeof(foo));
170 lseek (map->fd, 0, SEEK_SET);
171 dontcare = write (map->fd, &foo, sizeof(foo));
172 }
173
174 // Although the docs say we can close the fd right after mmap,
175 // while leaving the mmap outstanding even though the fd is closed,
176 // actual testing versus several kernels shows this leads to
177 // broken behavior. So, we close here instead.
178 //
179 close (map->fd);
180 // fprintf (stderr, "U");
181 #else // CRM_WINDOWS
182 FlushViewOfFile(map->addr, 0);
183 UnmapViewOfFile(map->addr);
184 CloseHandle(map->mapping);
185 CloseHandle(map->fd);
186 #endif
187
188 }
189
190 /////////////////////////////////////////////////////
191 //
192 // Hard-unmap by filename. Do this ONLY if you
193 // have changed the file by some means outside of
194 // the mmap system (i.e. by writing via fopen/fwrite/fclose).
195 //
crm_force_munmap_filename(char * filename)196 void crm_force_munmap_filename (char *filename)
197 {
198 CRM_MMAP_CELL *p;
199 // Search for the file - if it's already mmaped, unmap it.
200 // Note that this is a while loop and traverses the list.
201 for (p = cache; p != NULL; p = p->next)
202 {
203 if (strcmp(p->name, filename) == 0)
204 {
205 // found it... force an munmap.
206 crm_force_munmap_addr (p->addr);
207 //This was commented out and I uncommented it.
208 //I'm not sure why it was commented out,
209 //but it was definitely creating a seg fault
210 //during some testing. I hope that's ok. -JB
211 break; // because p may be clobbered during unmap.
212 }
213 }
214 }
215
216
217 //////////////////////////////////////////////////////
218 //
219 // Hard-unmap by address. Do this ONLY if you
220 // have changed the file by some means outside of
221 // the mmap system (i.e. by writing via fopen/fwrite/fclose).
222 //
crm_force_munmap_addr(void * addr)223 void crm_force_munmap_addr (void *addr)
224 {
225 CRM_MMAP_CELL *p;
226
227 // step 1- search the mmap cache to see if we actually have this
228 // mmapped
229 //
230 p = cache;
231 while ( p != NULL && p->addr != addr)
232 p = p->next;
233
234 if ( ! p )
235 {
236 nonfatalerror5 ("Internal fault - this code has tried to force unmap memory "
237 "that it never mapped in the first place. ",
238 "Please file a bug report. ", CRM_ENGINE_HERE);
239 return;
240 }
241
242 // Step 2: we have the mmap cell of interest. Mark it for real unmapping.
243 //
244 p->unmap_count = UNMAP_COUNT_MAX + 1;
245
246 // Step 3: use the standard munmap to complete the unmapping
247 crm_munmap_file (addr);
248 return;
249 }
250
251
252 //////////////////////////////////////////////////////
253 //
254 // This is the wrapper around the "traditional" file unmap, but
255 // does cacheing. It keeps count of unmappings and only unmaps
256 // when it needs to.
257 //
crm_munmap_file(void * addr)258 void crm_munmap_file (void *addr)
259 {
260 CRM_MMAP_CELL *p;
261 struct stat statbuf;
262
263 // step 1- search the mmap cache to see if we actually have this
264 // mmapped
265 //
266 p = cache;
267 while ( p != NULL && p->addr != addr)
268 p = p->next;
269
270 if ( ! p )
271 {
272 nonfatalerror5 ("Internal fault - this code has tried to unmap memory "
273 "that either was never mapped in the first place, or "
274 "has already been unmapped. ",
275 "Please file a bug report. ", CRM_ENGINE_HERE);
276 return;
277 }
278
279 // Step 2: we have the mmap cell of interest. Do the right thing.
280 //
281 p->unmap_count = (p->unmap_count) + 1;
282 if (p->unmap_count > UNMAP_COUNT_MAX)
283 {
284 crm_unmap_file_internal (p);
285 //
286 // File now unmapped, take the mmap_cell out of the cache
287 // list as well.
288 //
289 if (p->prev != NULL)
290 p->prev->next = p->next;
291 else
292 cache = p->next;
293 if (p->next != NULL)
294 p->next->prev = p->prev;
295 free(p->name);
296 free(p);
297 }
298 else
299 {
300 if (p->prot & PROT_WRITE)
301 {
302 #ifndef CRM_WINDOWS
303 msync (p->addr, p->actual_len, MS_ASYNC | MS_INVALIDATE);
304 stat(p->name, &statbuf);
305 //Since WE did this update, update the modification time
306 //What we have in memory is still correct! -JB
307 p->modification_time = statbuf.st_mtime;
308 #else // CRM_WINDOWS
309 //unmap our view of the file, which will lazily write any
310 //changes back to the file
311 UnmapViewOfFile(p->addr);
312 //and remap so we still have it open
313 p->addr = MapViewOfFile(p->mapping, (p->mode &
314 MAP_PRIVATE)?FILE_MAP_COPY:((p->prot &
315 PROT_WRITE)?FILE_MAP_WRITE:FILE_MAP_READ), 0, 0, 0);
316 //if the remap failed for some reason, just free everything
317 // and get rid of this cached mmap entry.
318 if (p->addr == NULL)
319 {
320 CloseHandle(p->mapping);
321 CloseHandle(p->fd);
322 if (p->prev != NULL)
323 p->prev->next = p->next;
324 else
325 cache = p->next;
326 if (p->next != NULL)
327 p->next->prev = p->prev;
328 free(p->name);
329 free(p);
330 }
331 #endif
332 }
333 }
334 }
335
336
337 /////////////////////////////////////////////////////////
338 //
339 // Force an Unmap on every mmapped memory area we know about
crm_munmap_all()340 void crm_munmap_all()
341 {
342 while (cache != NULL)
343 {
344 cache->unmap_count = UNMAP_COUNT_MAX + 1;
345 crm_munmap_file (cache->addr);
346 }
347 }
348
349
350 //////////////////////////////////////////////////////////
351 //
352 // MMap a file in (or get the map from the cache, if possible)
353 // (length is how many bytes to get mapped, remember!)
354 //
355 // prot flags are in the mmap() format - that is, PROT_, not O_ like open.
356 // If you want the full file, pass -1 as requested_len, the result is in
357 // actual_len.
358
crm_mmap_file(char * filename,long start,long requested_len,long prot,long mode,long * actual_len)359 void *crm_mmap_file (char *filename,
360 long start, long requested_len, long prot, long mode,
361 long *actual_len)
362 {
363 CRM_MMAP_CELL *p;
364 long pagesize = 0, k;
365 struct stat statbuf;
366 #ifndef CRM_WINDOWS
367 mode_t open_flags;
368 #else // CRM_WINDOWS
369 DWORD open_flags = 0;
370 DWORD createmap_flags = 0;
371 DWORD openmap_flags = 0;
372 #endif // CRM_WINDOWS
373
374 pagesize = 0;
375 // Search for the file - if it's already mmaped, just return it.
376 for (p = cache; p != NULL; p = p->next)
377 {
378 if (strcmp(p->name, filename) == 0
379 && p->prot == prot
380 && p->mode == mode
381 && p->start == start
382 && p->requested_len == requested_len)
383 {
384 // check the mtime; if this differs between cache and stat
385 // val, then someone outside our process has played with the
386 // file and we need to unmap it and remap it again.
387 int k;
388 struct stat statbuf;
389 k = stat (filename, &statbuf);
390 if (k != 0
391 || p->modification_time < statbuf.st_mtime)
392 {
393 // yep, someone played with it. unmap and remap
394 crm_force_munmap_filename (filename);
395 }
396 else
397 {
398 // nope, it looks clean. We'll reuse it.
399 if (actual_len)
400 *actual_len = p->actual_len;
401 return (p->addr);
402 }
403 }
404 }
405
406 // No luck - we couldn't find the matching file/start/len/prot/mode
407 // We need to add an mmap cache cell, and mmap the file.
408 //
409 p = (void *) malloc( sizeof ( CRM_MMAP_CELL) );
410 if (p == NULL)
411 {
412 untrappableerror5(" Unable to malloc enough memory for mmap cache. ",
413 " This is unrecoverable. Sorry.", CRM_ENGINE_HERE);
414 return MAP_FAILED;
415 }
416 p->name = strdup(filename);
417 p->start = start;
418 p->requested_len = requested_len;
419 p->prot = prot;
420 p->mode = mode;
421
422 #ifndef CRM_WINDOWS
423
424 open_flags = O_RDWR;
425 if ( ! (p->prot & PROT_WRITE) && (p->prot & PROT_READ) )
426 open_flags = O_RDONLY;
427 if ( (p->prot & PROT_WRITE) && !(p->prot & PROT_READ))
428 open_flags = O_WRONLY;
429 if (internal_trace)
430 fprintf (stderr, "MMAP file open mode: %ld\n", (long) open_flags);
431
432 //I changed all this so that the modification time would be
433 //correct. -JB
434 k = stat (p->name, &statbuf);
435 if ( k != 0 )
436 {
437 free (p->name);
438 free (p);
439 if (actual_len)
440 *actual_len = 0;
441 return (MAP_FAILED);
442 }
443
444 if (user_trace)
445 fprintf (stderr, "MMAPping file %s for direct memory access.\n", filename);
446 p->fd = open (filename, open_flags);
447 if (p->fd < 0)
448 {
449 close (p->fd);
450 free(p->name);
451 free(p);
452 if (actual_len)
453 *actual_len = 0;
454 return MAP_FAILED;
455 }
456
457
458 p->actual_len = p->requested_len;
459
460 // If we didn't get a length, fill in the max possible length via statbuf
461 if (p->actual_len < 0)
462 p->actual_len = statbuf.st_size - p->start;
463
464 p->addr = mmap (NULL,
465 p->actual_len,
466 p->prot,
467 p->mode,
468 p->fd,
469 p->start);
470
471 //We want the modification time to be AFTER the mmap since that
472 //could change it (I assume) if we have a PROT_WRITE. So we need
473 //to stat the file again
474 k = stat (p->name, &statbuf);
475 p->modification_time = statbuf.st_mtime;
476
477 //fprintf (stderr, "M");
478
479 // we can't close the fd now (the docs say yes, testing says no,
480 // we need to wait till we're really done with the mmap.)
481 //close(p->fd);
482
483 if (p->addr == MAP_FAILED)
484 {
485 close (p->fd);
486 free(p->name);
487 free(p);
488 if (actual_len)
489 *actual_len = 0;
490 return MAP_FAILED;
491 }
492
493
494 #else // CRM_WINDOWS
495 if (p->mode & MAP_PRIVATE)
496 {
497 open_flags = GENERIC_READ;
498 createmap_flags = PAGE_WRITECOPY;
499 openmap_flags = FILE_MAP_COPY;
500 }
501 else
502 {
503 if (p->prot & PROT_WRITE)
504 {
505 open_flags = GENERIC_WRITE;
506 createmap_flags = PAGE_READWRITE;
507 openmap_flags = FILE_MAP_WRITE;
508 }
509 if (p->prot & PROT_READ)
510 {
511 open_flags |= GENERIC_READ;
512 if (!(p->prot & PROT_WRITE))
513 {
514 createmap_flags = PAGE_READONLY;
515 openmap_flags = FILE_MAP_READ;
516 }
517 }
518 }
519 if (internal_trace)
520 fprintf (stderr, "MMAP file open mode: %ld\n", (long) open_flags);
521
522 //GROT GROT GROT
523 // this section was wrong under non-windows and the result was that
524 // the modification time was messed up. I don't change code I can't
525 // test, but someone with windows should fix this. Specifically, I
526 // see no place the modification time is update, which seems like a
527 // bug. -JB
528
529 // If we need to, we stat the file.
530 if (p->requested_len < 0)
531 {
532 long k;
533 k = stat (p->name, &statbuf);
534 if (k != 0)
535 {
536 free (p->name);
537 free (p);
538 if (actual_len)
539 *actual_len = 0;
540 return (MAP_FAILED);
541 };
542 };
543
544 if (user_trace)
545 fprintf (stderr, "MMAPping file %s for direct memory access.\n", filename);
546
547 p->fd = CreateFile(filename, open_flags, 0,
548 NULL, OPEN_EXISTING, 0, NULL);
549 if (p->fd == INVALID_HANDLE_VALUE)
550 {
551 free(p->name);
552 free(p);
553 return NULL;
554 }
555
556 p->actual_len = p->requested_len;
557 if (p->actual_len < 0)
558 p->actual_len = statbuf.st_size - p->start;
559
560 p->mapping = CreateFileMapping(p->fd,
561 NULL,
562 createmap_flags, 0, requested_len,
563 NULL);
564 if (p->mapping == NULL)
565 {
566 CloseHandle(p->fd);
567 free(p->name);
568 free(p);
569 return NULL;
570 }
571 p->addr = MapViewOfFile(p->mapping, openmap_flags, 0, 0, 0);
572 if (p->addr == NULL)
573 {
574 CloseHandle(p->mapping);
575 CloseHandle(p->fd);
576 free(p->name);
577 free(p);
578 return NULL;
579 }
580
581 {
582 SYSTEM_INFO info;
583 GetSystemInfo(&info);
584 pagesize = info.dwPageSize;
585 }
586
587 // Jaspan-san says force-loading every page is a good thing
588 // under Windows. I know it's a bad thing under Linux,
589 // so we'll only do it under Windows.
590 {
591 char one_byte;
592
593 char *addr = (char *) p->addr;
594 long i;
595 for (i = 0; i < p->actual_len; i += pagesize)
596 one_byte = addr[i];
597 }
598 #endif // CRM_WINDOWS
599
600 // If the caller asked for the length to be passed back, pass it.
601 if (actual_len)
602 *actual_len = p->actual_len;
603
604
605 // Now, insert this fresh mmap into the cache list
606 //
607 p->unmap_count = 0;
608 p->prev = NULL;
609 p->next = cache;
610 if (cache != NULL)
611 cache->prev = p;
612 cache = p;
613 return p->addr;
614 }
615
616 ///////////////////////////////////////////////////////////////////////
617 //
618 // End of section of code dual-licensed to Yerazunis and Jaspan
619 //
620 ///////////////////////////////////////////////////////////////////////
621
622
623
624 /////////////////////////////////////////////////////////////////////
625 //
626 // strntrn - translate characters of a string.
627 //
628 // Original spec by Bill Yerazunis, original code by Raul Miller,
629 // recode for CRM114 use by Bill Yerazunis.
630 //
631 // This code section (crm_strntrn and subsidiary routines) is
632 // dual-licensed to both William S. Yerazunis and Raul Miller,
633 // including the right to reuse this code in any way desired,
634 // including the right to relicense it under any other terms as
635 // desired.
636 //
637 //////////////////////////////////////////////////////////////////////
638 //
639 // We start out with two helper routines - one to invert a string,
640 // and the other to expand string ranges.
641 //
642 //////////////////////////////////////////////////////////////////////
643 //
644 // Given a string of characters, invert it - that is, the string
645 // that was originally 0x00 to 0xFF but with all characters that
646 // were in the incoming string omitted and the string repacked.
647 //
648 // Returns a pointer to the fresh inversion, or NULL (on error)
649 //
650 // The old string is unharmed. Be careful of it.
651 //
652 // REMEMBER TO FREE() THE RESULT OR ELSE YOU WILL LEAK MEMORY!!!
653
654
crm_strntrn_invert_string(unsigned char * str,long len,long * rlen)655 unsigned char * crm_strntrn_invert_string (unsigned char *str,
656 long len,
657 long *rlen)
658 {
659 unsigned char *outstr;
660 long i, j;
661
662 // create our output string space. It will never be more than 256
663 // characters. It might be less. But we don't care.
664 outstr = malloc (256);
665
666 // error out if there's a problem with MALLOC
667 if (!outstr)
668 {
669 untrappableerror5
670 ("Can't allocate memory to invert strings for strntrn", "",
671 CRM_ENGINE_HERE);
672 }
673
674 // The string of all characters is the inverse of "" (the empty
675 // string), so a mainline string of "^" inverts here to the string
676 // of all characters from 0x00 to 0xff.
677 //
678 // The string "^" (equivalent to total overall string "^^") is the
679 // string of all characters *except* ^; the mainline code suffices
680 // for that situation as well.
681 //
682 // BUT THEN how does one specify the string of a single "^"? Well,
683 // it's NOT of NOT of "NOT" ("^"), so "^^^" in the original, or
684 // "^^" here, is taken as just a literal "^" (one carat character).
685 //
686 if (len == 2 && strncmp ((char *)str, "^^", 2) == 0)
687 {
688 outstr[0] = '^';
689 *rlen = 1;
690 return (outstr);
691 };
692
693 // No such luck. Fill our map with "character present".
694 // fill it with 1's ( :== "character present")
695 //
696 for (i=0; i < 256; i++)
697 outstr[i] = 1;
698
699 // for each character present in the input string, zero the output string.
700 for (i = 0; i < len; i++)
701 outstr [ str [i]] = 0;
702
703 // outstr now is a map of the characters that should be present in the
704 // final output string. Since at most this is 1:1 with the map (which may
705 // have zeros) we can just reuse outstr.
706 //
707 for (i = 0, j = 0 ; i < 256; i++)
708 if (outstr[i])
709 {
710 outstr[j] = i;
711 j++;
712 };
713
714 // The final string length is j characters long, in outstr.
715 // Don't forget to free() it later. :-)
716
717 // printf ("Inversion: '%s' RLEN: %d\n", outstr, *rlen);
718 *rlen = j;
719 return (outstr);
720 }
721
722 // expand those hyphenated string ranges - input is str, of length len.
723 // We return the new string, and the new length in rlen.
724 //
crm_strntrn_expand_hyphens(unsigned char * str,long len,long * rlen)725 unsigned char * crm_strntrn_expand_hyphens(unsigned char *str,
726 long len,
727 long *rlen)
728 {
729 long j, k, adj;
730 unsigned char* r;
731
732 // How much space do we need for the expanded-hyphens string
733 // (note that the string might be longer than 256 characters, if
734 // the user specified overlapping ranges, either intentionally
735 // or unintentionally.
736 //
737 // On the other hand, if the user used a ^ (invert) as the first
738 // character, then the result is gauranteed to be no longer than
739 // 255 characters.
740 //
741 for (j= 1, adj=0; j < len-1; j++)
742 {
743 if ('-' == str[j])
744 {
745 adj+= abs(str[j+1]-str[j-1])-2;
746 }
747 }
748
749 // Get the string length for our expanded strings
750 //
751 *rlen = adj + len;
752
753 // Get the space for our expanded string.
754 r = malloc ( 1 + *rlen); /* 1 + to avoid empty problems */
755 if (!r)
756 {
757 untrappableerror5(
758 "Can't allocate memory to expand hyphens for strstrn",
759 "", CRM_ENGINE_HERE);
760 }
761
762 // Now expand the string, from "str" into "r"
763 //
764
765 for (j= 0, k=0; j < len; j++)
766 {
767 r[k]= str[j];
768 // are we in a hyphen expression? Check edge conditions too!
769 if ('-' == str[j] && j > 0 && j < len-1)
770 {
771 // we're in a hyphen expansion
772 if (j && j < len)
773 {
774 int delta;
775 int m = str[j-1];
776 int n = str[j+1];
777 int c;
778
779 // is this an increasing or decreasing range?
780 delta = m < n ? 1 : -1;
781
782 // run through the hyphen range.
783 if (m != n)
784 {
785 for (c= m+delta; c != n; c+= delta)
786 {
787 r[k++]= (unsigned char) c;
788 };
789 r[k++]= n;
790 }
791 j+= 1;
792 }
793 }
794 else
795 {
796 // It's not a range, so we just move along. Move along!
797 k++;
798 }
799 };
800
801 // fprintf (stderr, "Resulting range string: %s \n", r);
802 // return the char *string.
803 return (r);
804 }
805
806 // strntrn - translate a string, like tr() but more fun.
807 // This new, improved version not only allows inverted ranges
808 // like 9-0 --> 9876543210 but also negation of strings and literals
809 //
810 // flag of CRM_UNIQUE means "uniquify the incoming string"
811 //
812 // flag of CRM_LITERAL means "don't interpret the alteration string"
813 // so "^" and "-" regain their literal meaning
814 //
815 // The modification is "in place", and datastrlen gets modified.
816 // This routine returns a long >=0 strlen on success,
817 // and a negative number on failure.
818
strntrn(unsigned char * datastr,long * datastrlen,long maxdatastrlen,unsigned char * fromstr,long fromstrlen,unsigned char * tostr,long tostrlen,long flags)819 long strntrn (
820 unsigned char *datastr,
821 long *datastrlen,
822 long maxdatastrlen,
823 unsigned char *fromstr,
824 long fromstrlen,
825 unsigned char *tostr,
826 long tostrlen,
827 long flags)
828 {
829 long len= *datastrlen;
830 long flen, tlen;
831 unsigned char map[256];
832 unsigned char *from = NULL;
833 unsigned char *to = NULL;
834 long j, k, last;
835
836 // If tostrlen == 0, we're deleting, except if
837 // ASLO fromstrlen == 0, in which case we're possibly
838 // just uniquing or maybe not even that.
839 //
840 int replace = tostrlen;
841
842 // Minor optimization - if we're just uniquing, we don't need
843 // to do any of the other stuff. We can just return now.
844 //
845 if (tostrlen == 0 && fromstrlen == 0)
846 {
847 // fprintf (stderr, "Fast exit from strntrn \n");
848 *datastrlen = len;
849 return (len);
850 };
851
852
853 // If CRM_LITERAL, the strings are ready, otherwise build the
854 // expanded from-string and to-string.
855 //
856 if (CRM_LITERAL & flags)
857 {
858 // Else - we're in literal mode; just copy the
859 // strings.
860 from = malloc (fromstrlen);
861 strncpy ( (char *)from, (char *)fromstr, fromstrlen);
862 flen = fromstrlen;
863 to = malloc (tostrlen);
864 strncpy ((char *) to, (char *)tostr, tostrlen);
865 tlen = tostrlen;
866 if (from == NULL || to == NULL) return (-1);
867 }
868 else
869 {
870 // Build the expanded from-string
871 if (fromstr[0] != '^')
872 {
873 from = crm_strntrn_expand_hyphens(fromstr, fromstrlen, &flen);
874 if (!from) return (-1);
875 }
876 else
877 {
878 unsigned char *temp;
879 long templen;
880 temp = crm_strntrn_expand_hyphens(fromstr+1, fromstrlen-1, &templen);
881 if (!temp) return (-1);
882 from = crm_strntrn_invert_string (temp, templen, &flen);
883 if (!from) return (-1);
884 free (temp);
885 };
886
887 // Build the expanded to-string
888 //
889 if (tostr[0] != '^')
890 {
891 to = crm_strntrn_expand_hyphens(tostr, tostrlen, &tlen);
892 if (!to) return (-1);
893 }
894 else
895 {
896 unsigned char *temp;
897 long templen;
898 temp = crm_strntrn_expand_hyphens(tostr+1, tostrlen-1, &templen);
899 if (!temp) return (-1);
900 to = crm_strntrn_invert_string (temp, templen, &tlen);
901 if (!to) return (-1);
902 free (temp);
903 };
904 };
905
906 // If we're in <unique> mode, squish out any duplicated
907 // characters in the input data first. We can do this as an in-place
908 // scan of the input string, and we always do it if <unique> is
909 // specified.
910 //
911 if (CRM_UNIQUE & flags)
912 {
913 unsigned char unique_map [256];
914
915 // build the map of the uniqueable characters
916 //
917 for (j = 0; j < 256; j++)
918 unique_map[j] = 1; // all characters are keepers at first...
919 for (j = 0; j < flen; j++)
920 unique_map[from[j]] = 0; // but some need to be uniqued.
921
922 // If the character has a 0 the unique map,
923 // and it's the same as the prior character,
924 // don't copy it. Just move along.
925
926 for (j= 0, k= 0, last= -1; j < len; j++)
927 {
928 if (datastr[j] != last || unique_map[datastr[j]] )
929 {
930 last= datastr[k++]= datastr[j];
931 };
932 };
933 len= k;
934 };
935
936 // Minor optimization - if we're just uniquing, we don't need
937
938 // Build the mapping array
939 //
940 if (replace)
941 {
942 // This is replacement mode (not deletion mode) so we need
943 // to build the character map. We
944 // initialize the map as each character maps to itself.
945 //
946 for (j= 0; j < 256; j++)
947 {
948 map[j]= (unsigned char)j;
949 }
950
951 // go through and mod each character in the from-string to
952 // map into the corresponding character in the to-string
953 // (and start over in to-string if we run out)
954 //
955 for (j= 0, k=0; j < flen; j++)
956 {
957 map[from[j]]= to[k];
958 // check- did we run out of characters in to-string, so
959 // that we need to start over in to-string?
960 k++;
961 if (k >= tlen)
962 {
963 k= 0;
964 }
965 }
966
967
968 // Finally, the map is ready. We go thorugh the
969 // datastring translating one character at a time.
970 //
971 for (j= 0; j < len; j++)
972 {
973 datastr[j]= map[datastr[j]];
974 }
975 }
976 else
977 {
978 // No, we are not in replace mode, rather we are in delete mode
979 // so the map now says whether we're keeping the character or
980 // deleting the character.
981 for (j= 0; j < 256; j++)
982 {
983 map[j]= 1;
984 }
985 for (j= 0; j < flen; j++)
986 {
987 map[from[j]] = 0;
988 }
989 for (j= 0, k= 0; j < len; j++)
990 {
991 if (map[datastr[j]])
992 {
993 datastr[k++]= datastr[j];
994 }
995 }
996 len= k;
997 }
998
999 // drop the storage that we allocated
1000 //
1001 free(from);
1002 free(to);
1003 *datastrlen = len;
1004 return (len);
1005 }
1006
1007 /////////////////////////////////////////////////////////////////
1008 //
1009 // END of strntrn code (dual-licensed to both Yerazunis
1010 // and Miller
1011 //
1012 //////////////////////////////////////////////////////////////////
1013