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