1 //	crm_var_hash_table.c - handle variable hash tables
2 
3 // Copyright 2001-2009 William S. Yerazunis.
4 // This file is under GPLv3, as described in COPYING.
5 
6 //  include some standard files
7 #include "crm114_sysincludes.h"
8 
9 //  include any local crm114 configuration file
10 #include "crm114_config.h"
11 
12 //  include the crm114 data structures file
13 #include "crm114_structs.h"
14 
15 //  and include the routine declarations file
16 #include "crm114.h"
17 
18 //    the globals used when we need a big buffer  - allocated once, used
19 //    wherever needed.  These are sized to the same size as the data window.
20 extern char *tempbuf;
21 
22 
23 //      initialize the variable hash table (the vht)
24 //      and stuff in the "standards" (:_vars:, environment vars)
25 //
crm_vht_init(int argc,char ** argv)26 void crm_vht_init (int argc, char **argv)
27 {
28   long i, j, k;
29   long uvstart = 0;   // uvstart is the arg that the user sees (post "--")
30   long uvlist = 0;
31   char uvset[MAX_VARNAME];
32   extern char **environ;
33   char posvars[MAX_VARNAME];
34 
35 
36  //   create the variable hash table (one big one, shared )
37   vht = (VHT_CELL **) malloc (sizeof (VHT_CELL *) * vht_size);
38   if (!vht)
39     untrappableerror5("Couldn't malloc VHT cell.\n",
40 		      "No VHT cells, no variables, so no can run.  Sorry.",
41 		      CRM_ENGINE_HERE);
42   for (i = 0; i < vht_size; i++)
43     vht[i] = NULL;
44 
45 
46   //    initialize the temporary (non-data-window) area...
47   tdw = malloc (sizeof (CSL_CELL));
48   if (!tdw)
49     untrappableerror5 ("Couldn't malloc tdw.\n"
50 		     "We need the TDW for isolated variables."
51 		       "Can't continue.  Sorry.\n","", CRM_ENGINE_HERE);
52   tdw->filename = NULL;
53   tdw->rdwr = 1;
54   tdw->filedes = -1;
55   tdw->filetext = malloc (sizeof (char) * data_window_size);
56   if (!tdw->filetext)
57       untrappableerror5("Couldn't malloc tdw->filetext.\n"
58 		       "Without this space, you can't have any isolated "
59 			"variables,\n and we're stuck.  Sorry.","",
60 			CRM_ENGINE_HERE);
61   tdw->filetext[0] = '\000';
62   tdw->nchars = 0;
63   tdw->hash = 0;
64   tdw->mct = NULL;
65   tdw->nstmts = -1;
66   tdw->cstmt = -1;
67   tdw->caller = NULL;
68 
69   //    install a few constants.
70 
71   crm_set_temp_var (":_nl:", "\n");
72   crm_set_temp_var (":_ht:", "\t");
73   crm_set_temp_var (":_bs:", "\b");
74   crm_set_temp_var (":_sl:", "/");
75   crm_set_temp_var (":_sc:", ";");
76   crm_set_temp_var (":_cd:", "0");
77   crm_set_temp_var ("::", " ");
78 
79   //   put the version string in as a variable.
80   {
81     char verstr[1025];
82     verstr[0] = 0;
83     strcat (verstr, VERSION);
84     strcat (verstr, " ( ");
85     strcat (verstr, crm_regversion());
86     strcat (verstr, " )");
87     crm_set_temp_var (":_crm_version:", verstr);
88   };
89 
90   //
91   //    install the argc and argv values; restart argv values from [2]
92   //    if a "--" metaflag is seen.
93   //
94   //    argv[0] and argv[1] are not overrideable by "--".
95   crm_set_temp_var ( ":_arg0:", argv[0] );
96   crm_set_temp_var ( ":_arg1:", argv[1] );
97 
98   //     Check to see if there's a "--" arg.  If so, mark uvstart
99   //   (that is, "user var start" at that point)... but only the first "--".
100   {
101     long i, j;
102     uvstart = 2;
103     i = 0; j = 0;
104     for (i = 2; argc > i; i++)
105       {
106 	//   Check for the "--" metaflag
107 	if (strlen (argv[i]) == 2
108 	    && strncmp (argv[i], "--", 2) == 0
109 	    && uvstart == 2)
110 	  {
111 	    if (internal_trace)
112 	      fprintf (stderr, "Resetting uvstart counter to 2\n");
113 	    uvstart = i+1;
114 	  };
115       }
116   };
117 
118   //       The user variables start at argv[uvstart]
119   {
120     long i, j;
121     char anamebuf [255];
122     j = 2;
123     for ( i = uvstart; argc > i; i++ )
124       {
125 	sprintf (anamebuf, ":_arg%ld:", j);
126 	crm_set_temp_var ( anamebuf, argv[i] );
127 	j++;
128       };
129     //
130     //    and put the "user-visible" argc into a var as well.
131     sprintf (anamebuf, "%ld", j );
132     crm_set_temp_var (":_argc:", anamebuf);
133     //
134     //   Go through argv, and place positional arguments (that is,
135     //   arguments that don't contain any '-' preambles) into
136     //   :_pos0:, :_pos1:, ...
137     //
138     //   :_pos0: is always the name of the CRM114 engine.
139     //   :_pos1: is always the name of the program being run.
140     //   :_pos2: and so on are the command line args.
141     //
142     //    prepare to treasure up the positional args
143     posvars[0] = '\000';
144     j = 0;
145 
146     for ( i = uvstart; i < argc ; i++ )
147       {
148 	//
149 	//   check for the "-" sign; this is a positional argument only
150 	//                     if there is no "-" sign.
151 	if (argv[i][0] != '-')
152 	  {
153 	    sprintf (anamebuf, ":_pos%ld:", j);
154 	    crm_set_temp_var ( anamebuf, argv[i] );
155 	    j++;
156 	    if (j>0)    strcat (posvars, " ");
157 	    strcat (posvars, argv[i]);
158 	  };
159       };
160     sprintf (anamebuf, "%ld", j);
161     crm_set_temp_var (":_posc:", anamebuf);
162     crm_set_temp_var (":_pos_str:", posvars);
163     //
164     //   and set the fault to be a null string for now.
165     crm_set_temp_var (":_fault:", "");
166     //
167     //   set the current line number to a set of zeroes...
168     crm_set_temp_var (":_cs:", "00000000");
169     //
170     //   Set the "lazy" intermediate variable to just a space.
171     //   This will get rebound to point to the active lazy var.
172     crm_set_temp_var (":_lazy:", " ");
173 
174     //    set the current pid and parent pid.
175     {
176       char pidstr [32];
177       long pid;
178       pid = (long) getpid();
179       sprintf (pidstr, "%ld", pid);
180       crm_set_temp_var (":_pid:", pidstr);
181 #ifndef CRM_WINDOWS
182       pid = (long) getppid();
183       sprintf (pidstr, "%ld", pid);
184       crm_set_temp_var (":_ppid:", pidstr);
185 #endif	// !CRM_WINDOWS
186     }
187 
188 
189   };
190 
191   //      now, we shove the whole contents of the ENVIRON
192   //      vector into the VHT.
193 
194   i = 0;
195   tempbuf[0] = '\000';
196   if ( ! ignore_environment_vars)
197     while (environ [i])
198       {
199 	char *name;
200 	char *value ;
201 	j = 0;
202 	if (strlen (tempbuf) + strlen (environ[i]) < (data_window_size - 1000))
203 	  {
204 	    strcat (tempbuf, environ[i]);
205 	    strcat (tempbuf, "\n");
206 	  }
207 	else
208 	  untrappableerror5 ("The ENVIRONMENT variables don't fit into the "
209 			     "available space. \nThis is very broken.  Try "
210 			     "a larger data window (with flag -w NNNNN), \nor "
211 			     "drop the environment vars with "
212 			     "the (with flag -e)", "", CRM_ENGINE_HERE);
213 	while (environ[i][j] != '=') j++;
214 	name = (char *) malloc ((sizeof (char)) * (j+200));
215         if (!name)
216           untrappableerror5("Couldn't malloc :_env_ space."
217 			   "Can't continue.\n","", CRM_ENGINE_HERE);
218 	strcpy (name, ":_env_");
219 	memmove (&(name[strlen(name)]), &(environ[i][0]), j);
220 	name[j+6] = '\000';
221 	strcat (name, ":");
222 	j++;                  //  step past the equals sign.
223 	k = 0;
224 	value = strdup (&(environ[i][j+k]));
225 	crm_set_temp_var (name, value);
226 	free (name);
227 	free (value);
228 	i++;  //   and do the next environment variable
229       };
230   crm_set_temp_var (":_env_string:", tempbuf);
231 
232   //    see if argv [1] is a '-( whatever) arg, which limits the
233   //    set of runtime parameters allowed on the command line.
234   //    If so, we have the limit list.  We put spaces around the
235   //    args so we can just use strstr(3) to see if an arg is permitted
236   //    or if we should fault out.  Note that at this point,
237   //    we've trashed the contents of uvlist (the parens and the
238   //    trailing '--', if there was one.
239   //
240   if (strncmp (argv[1], "-(", 2) == 0)
241     {
242       long closepos;
243       uvlist = 1;
244       strcpy (uvset, " ");
245       strncat (uvset, &argv[1][2], strlen (argv[1]) - 3);
246       //   nuke the closing paren
247       closepos = 2;
248       while (uvset[closepos] != ')' && uvset[closepos] != '\000')
249 	closepos++;
250       uvset[closepos] = '\000';
251       strcat (uvset, " ");
252       if (user_trace) fprintf (stderr, "UVset: =%s=\n", uvset);
253     }
254   //
255   //
256   //   go through argv again, but this time look for "--foo"
257   //   and "--foo=bar" args.
258   //
259   {
260     long i, j, k;
261     char anamebuf [MAX_VARNAME];
262     char avalbuf [MAX_VARNAME];
263     long isok;
264     i = 0; j = 0; k = 0;
265     for ( i = uvstart; argc > i; i++ )
266       {
267 	//   check for the "--" metaflag preamble
268 	if (strlen ( argv[i] ) > 2 && strncmp (argv[i], "--", 2) == 0)
269 	  {
270 	    isok = 1;
271 	    if (uvlist == 1)
272 	      {
273 		isok = 0;
274 		//   build a testable name out of the -- flagname
275 		strcpy (anamebuf, " ");
276 		j=2; k = 1;
277 		while (argv[i][j] != '\000' && argv[i][j] != '=')
278 		  {
279 		    anamebuf[k] = argv[i][j];
280 		    j++;
281 		    k++;
282 		  };
283 		anamebuf[k] = 0;
284 		strcat (anamebuf, " ");
285 		//
286 		//       now we have the var name, surrounded by spaces
287 		//       we strstr() it to see if it's allowed or not.
288 		if (strstr(uvset, anamebuf))  isok = 1;
289 		//
290 		//        Well, maybe the name by itself is too loose;
291 		//        also allow name=value
292 		strcpy (anamebuf, " ");
293 		strcat (anamebuf, &argv[i][2]);
294 		strcat (anamebuf, " ");
295 		if (strstr(uvset, anamebuf))  isok = 1;
296 	      }
297 	    if (isok)
298 	      {
299 		if (internal_trace)
300 		  fprintf (stderr, "setting cmdline string %s", argv[i]);
301 		strcpy (avalbuf, "SET");
302 		j = 2; k = 0;
303 		//  copy the varname into anamebuf
304 		anamebuf[k] = ':';
305 		k++;
306 		while (argv[i][j] != '\000' && argv[i][j] != '=')
307 		  {
308 		    anamebuf[k] = argv[i][j];
309 		    j++;
310 		    k++;
311 		  };
312 		anamebuf[k] = ':';
313 		k++;
314 		anamebuf[k] = '\000';
315 		if (argv[i][j] == '=')
316 		  {
317 		    j++;      //  skip over the = sign
318 		    k = 0;
319 		    while (argv[i][j] != '\000')
320 		      {
321 			avalbuf[k] = argv[i][j];
322 			j++;
323 			k++;
324 		      }
325 		    avalbuf [k] = '\000';
326 		  }
327 		if (user_trace)
328 		  fprintf (stderr, "\n Setting cmdline var '%s' to '%s'\n",
329 			   anamebuf, avalbuf);
330 		crm_set_temp_var ( anamebuf, avalbuf );
331 	      }
332 	    else
333 	      {
334 		fprintf (stderr,
335 			 "\n ***Warning*** "
336 			 "This program does not accept the "
337 			 "flag '%s' , \n", anamebuf);
338 		fprintf (stderr,
339 			 " so we'll just ignore it for now. \n");
340 	      };
341 	  };
342       };
343   };
344 }
345 //           routine to put a variable into the temporary (tdw)
346 //           buffer.  names and values end up interleaved
347 //           sequentially, separated by newlines.  TDW really should have
348 //           been called the idw (Isolated Data Window) but it's too
349 //           late to fix it now.
350 //
351 //
crm_set_temp_nvar(char * varname,char * value,long vallen)352 void crm_set_temp_nvar (char *varname, char *value, long vallen)
353 {
354   long namestart, namelen;
355   long valstart;
356   long i;
357   long vnidx, vnlen;
358 
359   //       do the internal_trace thing
360   if (internal_trace)
361     fprintf (stderr, "  setting temp-area variable %s to value %s\n",
362 	     varname, value);
363 
364   i = crm_nextword (varname,strlen (varname), 0, &vnidx, &vnlen);
365   if ( i == 0)
366     {
367       nonfatalerror5 ("Somehow, you are assigning a value to a variable with",
368 		     "an unprintable name.  I'll permit it for now, but"
369 		      "your program is probably broken.", CRM_ENGINE_HERE);
370     };
371 
372   if ( (strlen (varname) + vallen + tdw->nchars + 1024) > data_window_size)
373     {
374       nonfatalerror5 ("This program has overflowed the ISOLATEd data "
375 		  "area with a variable that's just too big.  We'll  "
376                   "clip the tail off the string to fit in available memory.  "
377 		  "The big bad variable was named: ",
378 		   varname, CRM_ENGINE_HERE);
379       vallen = data_window_size - (strlen (varname)) - tdw->nchars - 1024;
380       if (vallen < 1)
381 	fatalerror5 ("Your program is so low on memory that it could not "
382                      "even clip the big variable.  This is really bad. "
383                      "The evil variable was named: " ,
384                      varname, CRM_ENGINE_HERE);
385     };
386 
387   //       check- is this the first time we've seen this variable?  Or
388   //       are we re-assigning a previous variable?
389   i = crm_vht_lookup (vht, &varname[vnidx], vnlen);
390   if (vht[i] == NULL)
391     {
392       //  never assigned this variable before, so we stick it in the
393       //  tdr window.
394       //
395       //       do the name first.  Start with a newline.
396       //  GROT GROT GROT
397       tdw->filetext[tdw->nchars] = '\n';
398       tdw->nchars++;
399       namestart = tdw->nchars;
400       namelen = vnlen;
401       memmove (&(tdw->filetext[tdw->nchars]), &(varname[vnidx]), namelen);
402       tdw->nchars = tdw->nchars + namelen;
403       //
404       //        and add a separator to prevent the varname from sharing
405       //        an endpoint offset with the var value.
406       tdw->filetext[tdw->nchars] = '=';
407       tdw->nchars++;
408       //
409       //       and the value second
410       valstart = tdw->nchars;
411       memmove (&tdw->filetext [tdw->nchars], value, vallen);
412       tdw->nchars = tdw->nchars + vallen;
413       //
414       //       add a separator again, so we don't get strings with overlapped
415       //       ranges into the var hash table
416       tdw->filetext[tdw->nchars] = ' ';
417       tdw->nchars++;
418       //
419       //        and put a NUL at the end of the tdw, so debuggers won't get
420       //       all bent out of shape.
421       tdw->filetext[tdw->nchars] = '\000';
422       //
423       //      now, we whack the actual VHT.
424       crm_setvar (NULL, 0,
425 		  tdw->filetext, namestart, namelen,
426 		  tdw->filetext, valstart, vallen,
427 		  0, 0);
428       //     that's it.
429 
430     }
431   else
432     {
433       //   This variable is preexisting.  Perform an ALTER on it.
434       //
435       crm_destructive_alter_nvariable ( &varname[vnidx], vnlen,
436 				       value, vallen );
437     };
438 }
439 
440 //     GROT GROT GROT this routine needs to replaced for 8-bit-safeness.
441 //     Use ONLY where you can be sure no embedded NULs will be seen (i.e.
442 //     fixed strings in the early startup.
443 //
crm_set_temp_var(char * varname,char * value)444 void crm_set_temp_var (char *varname, char *value)
445 {
446   crm_set_temp_nvar (varname, value, strlen (value));
447 }
448 
449 
450 
451 //           routine to put a data-window-based (the cdw, that is)
452 //           variable into the VHT.  The text of the variable's name
453 //           goes into the tdw buffer, and the value stays in the main
454 //           data window (cdw) buffer.
455 //
456 //           This is equivalent to a "bind" operation - that is, the
457 //           pointers move around, but the data window doesn't get
458 //           changed.
459 //
460 //           Note - if you rebind a var, you should consider if your
461 //           routine should also evaluate the old area for reclamation.
462 //           (reclamation uses "crm_compress_tdw_section", see comments
463 //           further down in the code here)
464 
crm_set_windowed_nvar(char * varname,long varlen,char * valtext,long start,long len,long stmtnum)465 void crm_set_windowed_nvar ( char *varname,
466 			     long varlen,
467 			     char *valtext,
468 			     long start,
469 			     long len,
470 			     long stmtnum)
471 {
472   long i;
473   long namestart, namelen;
474   //       do the internal_trace thing
475   if (internal_trace)
476     {
477       long i;
478       fprintf (stderr, "  setting data-window variable %s to value ",
479 	     varname);
480       for (i = start; i < start+len; i++)
481 	fprintf (stderr, "%c", valtext[i]);
482       fprintf (stderr, "\n");
483     };
484 
485   //    check and see if the variable is already in the VHT
486   i = crm_vht_lookup (vht, varname, varlen);
487   if (vht[i] == NULL)
488     {
489       //  nope, never seen this var before, add it into the VHT
490       //       namestart is where we are now.
491       if (internal_trace)
492 	fprintf (stderr, "... new var\n");
493       //
494       //      Put the name into the tdw memory area, add a & after it.
495       //
496       //       do the name first.  Start on a newline.
497       tdw->filetext[tdw->nchars] = '\n';
498       tdw->nchars++;
499       namestart = tdw->nchars;
500       namelen = varlen;
501       memmove (&tdw->filetext[namestart], varname, varlen);
502       tdw->nchars = tdw->nchars + namelen;
503       //
504       //     put in an "&" separator
505       tdw->filetext[tdw->nchars] = '&';
506       tdw->nchars++;
507       //
508       //      now, we whack the actual VHT.
509       crm_setvar (NULL, 0,
510 		  tdw->filetext, namestart, namelen,
511 		  valtext, start, len,
512 		  stmtnum, 0);
513       //     that's it.
514     }
515   else
516     {
517       //     We've seen this var before.  But, there's a gotcha.
518       //     If the var _was_ in the tdw, but is now being moved back
519       //     to the cdw, or being rebound inside another tdw var,
520       //     then the prior var value might now be dead- that is, "leaked
521       //     memory", and now inaccessible.
522       //
523       {
524 
525 	//     move the text/start/len values around to accomodate the new
526 	//     value.
527 	//
528 	if (internal_trace)
529 	  fprintf (stderr, "... old var\n");
530 	crm_setvar (NULL, 0,
531 		    vht[i]->nametxt, vht[i]->nstart, vht[i]->nlen,
532 		    valtext, start, len,
533 		    stmtnum, 0);
534 
535 	//       Do we need to repair the leaked memory?  Only necessary if the
536 	//       old text was in the tdw area; this is harmless if the area
537 	//       is in use by another var, but if we have removed the last
538 	//       reference to any tdw-based vars, we ought to reclaim them..
539 	//
540 	//       NOTE - we don't do it here since synchronicity issues
541 	//       between a var being rebound, reclamation happening,
542 	//       and then another var _in the same match_ being bound
543 	//       (to a old, unupdated set of offsets) is such a pain.
544 	//
545 	//       Instead, routines using this routine should also be sure
546 	//       to call crm_compress_tdw_section if there's a chance they
547 	//       should be releasing TDW memory. AFTER they've done  ALL the
548 	//       rebinding.  That way, all indices and offsets are in the VHT
549 	//       where they can be safely updated.
550 	//
551       };
552     };
553 }
554 
555 //#define RECLAIM_ALL_EVERY_TIME 1
556 #ifdef RECLAIM_ALL_EVERY_TIME
557 //
558 //    How we compress out an area that might no longer be in use.
559 static long crm_recursive_compress_tdw_section
560 (char *oldtext, long oldstart, long oldend);
561 
crm_compress_tdw_section(char * oldtext,long oldstart,long oldend)562 long crm_compress_tdw_section (char *oldtext, long oldstart, long oldend)
563 {
564   //   let's court death, and do a FULL compress.
565   return (crm_recursive_compress_tdw_section
566 	  (tdw->filetext, 0, tdw->nchars + 1));
567 }
crm_recursive_compress_tdw_section(char * oldtext,long oldstart,long oldend)568 long crm_recursive_compress_tdw_section
569 (char *oldtext, long oldstart, long oldend)
570 #else	// !RECLAIM_ALL_EVERY_TIME
571 long crm_compress_tdw_section (char *oldtext, long oldstart, long oldend)
572 #endif	// !RECLAIM_ALL_EVERY_TIME
573 {
574 
575   //  The algorithm basically checks to see if there is any region of
576   //  the given tdw space that is not currently used by another var.
577   //  All such regions are reclaimed with a slice-n-splice.  We return
578   //  the number of reclaimed characters.
579   //
580   //  The algorithm starts out with start and end of the tenatively
581   //  unused "to be killed" region.  It checks each member of the VHT
582   //  in the TDW.  If the region overlaps, don't kill the overlapping
583   //  part of the region.  If at any time the region length goes to 0,
584   //  we know that there's no region left to kill.  (Option- if the
585   //  gap is less than MAX_RECLAIMER_GAP chars, we don't bother moving
586   //  it; we retain it as buffer.  This minimizes thrashing
587   //
588   //  NOTE that the END VALUES ONLY "oldend" and "newend" vars are
589   //  NON-inclusive, they index the first NON-involved character
590   //  (oldstart and newstart index "involved" characters, that we _do_
591   //  include in our strings)
592   //
593   //  BIG ISSUE: As coded, this routine needs to leave a _buffer_ of at
594   //  least one UNUSED character between each used (but isolated) string
595   //  area.  Knowing when to get rid of extra copies of this character
596   //  has been a big hassle.  Right now there may be a small leak here
597   //  so if you can find it, please let me know!   Note that any fix that
598   //  does not keep two adjacent isolated regions from merging (including
599   //  when the first or second becomes a zero-length string!) will get
600   //  the submittor a gentle smile and a pointer to this very comment.
601   //  (the reason being that prior code that did not leave a buffer
602   //  exhibited the property that if A and B were isolated but adjacent,
603   //  and then A shrank to 0 length, then B would share the same start
604   //  point, and an alteration to A would then *also* insert at the start
605   //  point of B, causing A and B to become NONisolated and space-sharing.
606   //  That said- enjoy the bug hunt.  :)
607 
608   long j, newstart, newend, reclaimed;
609 
610   j = newstart = newend = reclaimed = 0;
611 
612   //  return (0);
613 
614   j = newstart = newend = reclaimed = 0;
615   if (internal_trace)
616     fprintf (stderr, " [ Compressing isolated data.  Length %ld chars, "
617 	     "start %ld, len %ld ]\n",
618 	     tdw->nchars,
619 	     oldstart,
620 	     oldend - oldstart);
621 
622   //      If oldstart >= oldend, then there's no compression to be done.
623   //
624   if (oldstart >= oldend )
625     {
626       if (internal_trace)
627 	fprintf (stderr, " [ Zero-length compression string... don't do this! ]\n");
628       return (0);
629     }
630 
631   if (oldtext != tdw->filetext)
632     {
633       fatalerror5 (" Request to compress non-TDW data.  This is bogus. ",
634 		  " Please file a bug report", CRM_ENGINE_HERE);
635       return ( 0 );
636     };
637 
638   //    Look one character further to before and after;
639   //if (oldstart > 3)  oldstart --;
640   //if (oldend < data_window_size - 1) oldend ++;
641 
642   for (j = 0; j < vht_size; j++)
643     {
644       if (vht[j]    // is this slot in use?
645 	  && vht[j]->valtxt == tdw->filetext
646 	  //   Note that being part of :_iso: does NOT exclude from reclamation
647 	  &&  0 != strncmp (&vht[j]->nametxt[vht[j]->nstart], ":_iso:", 6 ))
648 	{
649 	  //    for convenience, we get nice short names:
650 	  newstart = vht[j]->vstart - 1;
651 	  newend = newstart + vht[j]->vlen + 2;
652 	  //  leave some space no matter what...
653 	  if (newend < newstart + 2) newend = newstart + 2;
654 
655 	  //    6 Possible cases:
656 	  //      dead zone entirely before current var
657 	  //      dead zone entirely after current var
658 	  //      dead zone entirely inside current var
659 	  //      dead zone overlaps front of current var
660 	  //      dead zone overlaps back of current var
661 	  //      dead zone split by current var
662 	  //
663 
664 	  //      1: dead zone entirely before current var
665 	  //
666 	  //       <os------------oe>
667 	  //                           <ns--------ne>
668 	  //
669 	  if ( oldend <= newstart)
670 	    {
671 	      //   nothing to be done here - not overlapping
672 	      goto end_of_vstring_tests;
673 	    };
674 
675 	  //      2: dead zone entirely after current var
676 	  //
677 	  //                        <os------------oe>
678 	  //       <ns--------ne>
679 	  //
680 	  if ( newend <= oldstart )
681 	    {
682 	      //   nothing to be done here - not overlapping
683 	      goto end_of_vstring_tests;
684 	    };
685 
686 	  //   If we get this far, the dead zone in some way overlaps with
687 	  //   our current variable.
688 
689 	  //      3: dead zone entirely inside a currently live var
690 	  //
691 	  //                 <os-------oe>
692 	  //              <ns----------------ne>
693 	  //
694 	  //      So we terminate this procedure (nothing can be reclaimed)
695 	  //
696 	  if (oldstart >= newstart && oldend <= newend)
697 	    {
698 	      //   the dead zone is inside a non-dead var, so
699 	      //   we can terminate our search right now.
700 	      if ( internal_trace)
701 		fprintf (stderr, " [ Compression not needed after all. ]\n");
702 	      return ( 0 );
703 	    };
704 
705 	  //      4: dead zone overlaps front of current var; we trim the
706 	  //      dead zone to not include the current var.
707 	  //
708 	  //       <os------------oe>
709 	  //              <ns--------ne>
710 	  //
711 	  if ( oldstart < newstart && oldend <= newend )
712 	    {
713 	      //   The dead zone should not include the part that's
714 	      //   also new variable.  So, we clip out the part
715 	      //   that's still active.
716 	      if ( internal_trace)
717 		fprintf (stderr, " [ Trimming tail off of compression. ]\n");
718 	      //
719 	      //     newstart is a "good" char, but since oldend is
720 	      //     noninclusive, this is right.
721 	      oldend = newstart;
722 	      goto end_of_vstring_tests;
723 	    };
724 
725 	  //      5: dead zone overlaps back of current var; trim the front off
726 	  //      the dead zone.
727 	  //
728 	  //                   <os------------oe>
729 	  //              <ns--------ne>
730 	  //
731 	  if (newstart <= oldstart && newend <= oldend)
732 	    {
733 	      if (internal_trace)
734 		fprintf (stderr, " [ Trimming head off of compression. ]\n");
735 	      //
736 	      //     Newend is the first char that ISN'T in the var, so this
737 	      //     is correct.
738 	      oldstart = newend ;
739 	      goto end_of_vstring_tests;
740 	    };
741 	  //      6: dead zone split by current var - the dead zone is actually
742 	  //      split into two distinct pieces.  In this case, we need to
743 	  //      recurse on the two pieces.
744 	  //
745 	  //         <os--------------------oe>
746 	  //               <ns--------ne>
747 	  //
748 	  if ( oldstart <= newstart && newend <= oldend )
749 	    {
750 	      if (internal_trace)
751 		{
752 		  fprintf (stderr, " [ Compression split ]\n");
753 		  fprintf (stderr, " [ First part will be %ld to %ld .]\n",
754 			   oldstart, newstart);
755 		  fprintf (stderr, " [ Second part will be %ld to %ld .]\n",
756 			   newend, oldend);
757 		};
758 	      //
759 	      //      Tricky bit here - we have to do the aft (ne-oe
760 	      //      section) first, so we don't move the os-ns
761 	      //      section offsets.
762 	      //
763 
764 		      //    was newend - 1, but should be same as case 3
765 		      //   above (dead zone overlaps tail)
766 #ifdef RECLAIM_ALL_EVERY_TIME
767 	      reclaimed = crm_recursive_compress_tdw_section (oldtext, newend, oldend);
768 	      reclaimed +=crm_recursive_compress_tdw_section(oldtext, oldstart, newstart);
769 #else	// ! RECLAIM_ALL_EVERY_TIME
770 	      reclaimed = crm_compress_tdw_section (oldtext, newend, oldend);
771 	      reclaimed +=crm_compress_tdw_section(oldtext, oldstart, newstart);
772 #endif	// ! RECLAIM_ALL_EVERY_TIME
773 	      //   Return here instead of executing common slice-and-splice
774 	      //   tail, because each of our recursive children will do
775 	      //   that for us.
776 	      return (reclaimed);
777 	    }
778 	}
779       //       and the semicolon to keep some compilers happy
780     end_of_vstring_tests: ;
781       //    Now, repeat with the name string - all name strings are protected
782       if (vht[j]
783 	  && vht[j]->nametxt == tdw->filetext)
784 	{
785 	  newstart = vht[j]->nstart - 1 ;
786 	  newend = newstart + vht[j]->nlen + 2;
787 	  //  leave some space no matter what...
788 	  if (newend < newstart + 4) newend = newstart + 2;
789 
790 	  //    Possible cases:
791 	  //      dead zone entirely before current var
792 	  //      dead zone entirely after current var
793 	  //      dead zone entirely inside current var
794 	  //      dead zone overlaps front of current var
795 	  //      dead zone overlaps back of current var
796 	  //      dead zone split by current var
797 	  //
798 	  //      dead zone entirely before current var
799 	  //
800 	  //       <os------------oe>
801 	  //                           <ns--------ne>
802 	  // OK
803 	  if ( oldend <= newstart)
804 	    {
805 	      //   nothing to be done here - not overlapping
806 	      goto end_of_nstring_tests;
807 	    };
808 
809 	  //      dead zone entirely after current var
810 	  //
811 	  //                        <os------------oe>
812 	  //       <ns--------ne>
813 	  //
814 	  if ( newend <= oldstart )
815 	    {
816 	      //   nothing to be done here - not overlapping
817 	      goto end_of_nstring_tests;
818 	    };
819 
820 	  //   If we get this far, the dead zone in some way overlaps with
821 	  //   our current variable.
822 
823 	  //      dead zone entirely inside a currently live var
824 	  //
825 	  //                 <os-------oe>
826 	  //              <ns----------------ne>
827 	  //
828 	  //      So we terminate this procedure (nothing can be reclaimed)
829 	  //
830 	  if (oldstart >= newstart && oldend <= newend)
831 	    {
832 	      //   the dead zone is inside a non-dead var, so
833 	      //   we can terminate our search right now.
834 	      if ( internal_trace)
835 		fprintf (stderr, " [ Compression not needed after all. ]\n");
836 	      return ( 0 );
837 	    };
838 
839 	  //      dead zone overlaps front of current var; we trim the
840 	  //      dead zone to not include the current var.
841 	  //
842 	  //       <os------------oe>
843 	  //              <ns--------ne>
844 	  //
845 	  if ( oldstart < newstart && oldend <= newend )
846 	    {
847 	      //   The dead zone should not include the part that's
848 	      //   also new variable.  So, we clip out the part
849 	      //   that's still active.
850 	      if ( internal_trace)
851 		fprintf (stderr, " [ Trimming tail off of compression. ]\n");
852 	      //
853 	      //     newstart is a "good" char, but since oldend is
854 	      //     noninclusive, this is right.
855 	      oldend = newstart;
856 	      goto end_of_nstring_tests;
857 	    };
858 
859 	  //      dead zone overlaps back of current var; trim the front off
860 	  //      the dead zone.
861 	  //
862 	  //                   <os------------oe>
863 	  //              <ns--------ne>
864 	  //
865 	  if (newstart <= oldstart && newend <= oldend)
866 	    {
867 	      if (internal_trace)
868 		fprintf (stderr, " [ Trimming head off of compression. ]\n");
869 	      //
870 	      //     Newend is the first char that ISN'T in the var, so this
871 	      //     is correct.
872 	      oldstart = newend ;
873 	      goto end_of_nstring_tests;
874 	    };
875 	  //      dead zone split by current var - the dead zone is actually
876 	  //      split into two distinct pieces.  In this case, we need to
877 	  //      recurse on the two pieces.
878 	  //
879 	  //         <os--------------------oe>
880 	  //               <ns--------ne>
881 	  //
882 	  if ( oldstart <= newstart && newend <= oldend )
883 	    {
884 	      if (internal_trace)
885 		{
886 		  fprintf (stderr, " [ Compression split ]\n");
887 		  fprintf (stderr, " [ First part will be %ld to %ld .]\n",
888 			   oldstart, newstart);
889 		  fprintf (stderr, " [ Second part will be %ld to %ld .]\n",
890 			   newend, oldend);
891 		};
892 	      //
893 	      //      Tricky bit here - we have to do the aft (ne-oe
894 	      //      section) first, so we don't move the os-ns
895 	      //      section offsets.
896 	      //
897 
898 		      //    was newend - 1, but should be same as case 3
899 		      //   above (dead zone overlaps tail)
900 #ifdef RECLAIM_ALL_EVERY_TIME
901 	      reclaimed = crm_recursive_compress_tdw_section (oldtext, newend, oldend);
902 	      reclaimed +=crm_recursive_compress_tdw_section (oldtext, oldstart, newstart);
903 #else	// ! RECLAIM_ALL_EVERY_TIME
904 	      reclaimed = crm_compress_tdw_section (oldtext, newend, oldend);
905 	      reclaimed +=crm_compress_tdw_section (oldtext, oldstart, newstart);
906 #endif	// ! RECLAIM_ALL_EVERY_TIME
907 	      //   Return here instead of executing common slice-and-splice
908 	      //   tail, because each of our recursive children will do
909 	      //   that for us.
910 	      return (reclaimed);
911 	    }
912 	  //       and the semicolon to keep some compilers happy
913 	end_of_nstring_tests: ;
914 	};
915     };
916   //
917   //   Well, we've now scanned the VHT, and oldstart/oldend are the
918   //   actual dead zone (storage that really isn't used).
919   //
920   //   So, we can compress this storage out with a slice-and-splice
921   //   return how many character cells we were able to reclaim.
922   //
923   {
924     long cutlen;
925     // cutlen is supposed to be negative for compress
926     cutlen = oldstart - oldend - 1;
927     if (cutlen > 0)
928       fatalerror5 ("Internal cut-length error in isolated var reclamation.",
929 		  "  Please file a bug report", CRM_ENGINE_HERE);
930 
931     //    Future Enhancement - dead zones of some small size should be
932     //    allowed to stay.  This would speed up WINDOW a lot. (but we
933     //    would need to expand the range of oldstart and oldend to
934     //    actually reclaim those areas if storage really ran low.
935     //    Maybe this should be compile-time or command-line parameter?)
936 
937     if (cutlen < 0)
938       {
939         if (internal_trace)
940 	  {
941 	    fprintf (stderr, " [ compression slice-splice at %ld for %ld chars. ]\n",
942 		     oldstart, cutlen);
943 	  }
944 	crm_slice_and_splice_window (tdw, oldstart, cutlen);
945 	if (internal_trace)
946 	  {
947 	    fprintf (stderr, " [ new isolated area will be %ld bytes. ]\n",
948 		     tdw->nchars);
949 	  };
950       };
951     return (- (cutlen));
952   };
953 }
954 
955 //
956 //     Destructive alteration of a preexisting variable, which can be
957 //     anywhere.  If the variable is not preexisting, we create it and
958 //     toss a nonfatal error.
959 //
960 
crm_destructive_alter_nvariable(char * varname,long varlen,char * newstr,long newlen)961 void crm_destructive_alter_nvariable (char *varname, long varlen,
962 				     char *newstr, long newlen)
963 {
964   long i;
965   long vhtindex, oldlen, delta;
966 
967   //      get the first variable name and verify it exists.
968   //     GROT GROT GROT this should use nextword!!!
969   i = 0;
970   while (varname[i] < 0x021 && i < varlen) i++;
971   vhtindex = crm_vht_lookup (vht, &(varname[i]), varlen);
972   if (vht[vhtindex] == NULL)
973     {
974       // IGNORE FOR NOW
975       nonfatalerror5 (" Attempt to alter the value of a nonexistent "
976       		    "variable, so I'm creating an ISOLATED variable.  "
977       		    "I hope that's OK.  The nonexistent variable is: ",
978       		    &(varname[i]), CRM_ENGINE_HERE);
979       crm_set_temp_var (&varname[i], "");
980     };
981 
982   //     make enough space in the input buffer to accept the new value
983   oldlen = vht[vhtindex]->vlen;
984   delta = newlen - oldlen;
985   mdw = NULL;
986   if (tdw->filetext == vht[vhtindex]->valtxt)
987     mdw = tdw;
988   if (cdw->filetext == vht[vhtindex]->valtxt)
989     mdw = cdw;
990 
991   //   GROT GROT GROT  get rid of this if we go to MAPped file vars.
992   if (mdw == NULL)
993     {
994       fatalerror5 (" Bogus text bloc containing variable : ",
995 		   varname, CRM_ENGINE_HERE);
996       goto bailout;
997     };
998 
999   //
1000   if (user_trace)  // major debug
1001     {
1002       long i;
1003       // fprintf (stderr, "\n     surgery on the var %s\n ", varname);
1004       fprintf (stderr, " surgery on the var >");
1005       for (i = 0; i < varlen; i++ )
1006 	fprintf (stderr, "%c", varname[i]);
1007       fprintf (stderr, "<\n");
1008       //fprintf (stderr, "new value is: \n***%s***\n", newstr);
1009       fprintf (stderr, " new value is ***>");
1010       for (i = 0; i < newlen; i++ )
1011 	fprintf (stderr, "%c", newstr[i]);
1012       fprintf (stderr, "<***\n");
1013     }
1014   //     slice and splice the mdw text area, to make the right amount of
1015   //     space...
1016   crm_slice_and_splice_window (mdw, vht[vhtindex]->vstart, delta);
1017   //
1018   //      Zap the mstart and mlen markers so that searches are reset to start
1019   //      of the variable.  Note that we have to do this _after_ we slice
1020   //      and splice, otherwise we mangle our own mstart and mlen.
1021   vht[vhtindex]->mstart = vht[vhtindex]->vstart;
1022   vht[vhtindex]->mlen = 0;
1023   //
1024   //     now we have space, and we can put in the characters from
1025   //     the new pattern
1026   memmove (&(mdw->filetext[vht[vhtindex]->vstart]),
1027 	   newstr,
1028 	   newlen);
1029 
1030   //   semicolon (null stmt) on next line to keep some compilers happy:
1031   //
1032  bailout: ;
1033 };
1034 
1035 
1036 //      Surgically lengthen or shorten a window.  The window pointed
1037 //      to by mdw gets delta extra characters added or cut at "where".
1038 //      (more precisely, just _before_ "where" - the insert/delet
1039 //      point is just before the "where'th" character, and the
1040 //      where'th character will be the first one moved.  If the
1041 //      allocated length is not enough, additional space can be
1042 //      malloced.  Finally, the vht is fixed up so everything still
1043 //      points "correctly".
1044 //
crm_slice_and_splice_window(CSL_CELL * mdw,long where,long delta)1045 void crm_slice_and_splice_window ( CSL_CELL *mdw, long where, long delta)
1046 {
1047   char *taildest;
1048   char *tailsrc;
1049   long taillen;
1050 
1051   //    these are to keep the compiler quiet.
1052   taildest = NULL;
1053   tailsrc = NULL;
1054   taillen = 0;
1055 
1056   if (delta + mdw->nchars > data_window_size - 10)
1057     {
1058       fatalerror5 (" Data window trying to get too long.",
1059 		   " Try increasing the data window maximum size.",
1060 		   CRM_ENGINE_HERE);
1061       goto bailout;
1062     };
1063 
1064   if (delta == 0)
1065     {
1066       if (internal_trace)
1067 	{
1068 	  fprintf (stderr, " zero delta, no buffer hackery required\n");
1069 	};
1070       return;
1071     };
1072 
1073   // bump chars in input window delta places
1074   if (internal_trace)
1075     {
1076       fprintf (stderr, "moving text in window %lx,", (long int) mdw->filetext);
1077       fprintf (stderr, " starting at %ld, ", where);
1078       fprintf (stderr, "delta length is %ld\n", delta);
1079     };
1080 
1081   if (delta > 0)
1082     {             // lengthening alteration...
1083       taildest = &(mdw->filetext[where + delta]);
1084       tailsrc = &(mdw->filetext[where]);
1085       taillen = mdw->nchars - where;
1086     };
1087 
1088   if (delta < 0)  //   shortening alteration
1089     {
1090       taildest = &(mdw->filetext[where]);
1091       tailsrc = &(mdw->filetext[where - delta]); //  delta is minus already!!
1092       taillen = mdw->nchars - where + delta;
1093       //      taillen = mdw->nchars + 1 - where;
1094 
1095     }
1096   if (internal_trace)
1097     fprintf (stderr,
1098 	     "buffer sliding, tailsrc: %lx, taildest: %lx, length: %ld\n",
1099 	     (long int) tailsrc, (long int) taildest, taillen);
1100 
1101   //     and move the actual data
1102   if (taillen + 1 > 0) memmove ( taildest, tailsrc, taillen + 1 );
1103 
1104   //     update the length of the window as well.
1105   mdw->nchars = mdw->nchars + delta;
1106 
1107   //      and update all of our captured variables to have the right ptrs.
1108   crm_updatecaptures (mdw->filetext,
1109 		      where,
1110 		      delta);
1111  bailout:
1112   //   GROT GROT GROT
1113   //   The following bit of absolutely meaningless code is just there
1114   //   so that some versions of the C compiler don't complain.  It does
1115   //   nothing.
1116   {
1117     delta = 0;
1118   }
1119 
1120 }
1121 
1122   //        allow_data_window_to_grow
1123 #ifdef no_dont_do_this_yet
1124   //    Grow the window to hold the incoming text, if needed.
1125   //    Grow it by 4x each time.
1126   while (delta + mdw->nchars > data_window_size - 1)
1127     {
1128       char *ndw;
1129       long odws, i;
1130       odws = data_window_size;
1131       data_window_size = 4 * data_window_size;
1132       nonfatalerror5 (" Data window trying to get too long.",
1133 		     " increasing data window... ", CRM_ENGINE_HERE);
1134       ndw = (char *) malloc ( data_window_size);
1135       if (!ndw)
1136 	untrappableerror5("Couldn't malloc ndw.  This is bad too.\n","",
1137 			  CRM_ENGINE_HERE);
1138 
1139       //  now copy the old data window into the new one
1140       memmove (ndw, mdw->filetext, odws);
1141 
1142       //   and update the outstanding pointers, like the ones in the
1143       //   vht...
1144       for (i = 0; i < vht_size; i++)
1145 	if (vht[i] != NULL)
1146 	  {
1147 	    if (vht[i]->nametxt == mdw->filetext)
1148 	      vht[i]->nametxt = ndw;
1149 	    if (vht[i]->valtxt == mdw->filetext)
1150 	      vht[i]->valtxt = ndw;
1151 	  };
1152 
1153       //    and lastly, point the cdw or tdw to the new larger window.
1154       free (mdw->filetext);
1155       mdw->filetext = ndw;
1156     };
1157 #endif	//  no_dont_do_this_yet
1158 
1159 
1160 //
1161 //    crm_vht_lookup - given a char *start, long len, varnam
1162 //    finds and returns the vht index of the variable
1163 //    or the index of the appropriate NULL slot to put
1164 //    the var in, if not found.
1165 
crm_vht_lookup(VHT_CELL ** vht,char * vname,long vlen)1166 long crm_vht_lookup (VHT_CELL **vht, char *vname, long vlen)
1167 {
1168   unsigned long hc;
1169   unsigned long i, j, k;
1170   int done;
1171   long vsidx;
1172   long vslen;
1173 
1174   j = 0;  // just so J is used.
1175 
1176   //   Consistency scan - look for those null varnames!  Do this every
1177   //   time!
1178   if (1)
1179     {
1180       long i, j;
1181       long corrupted;
1182       for (i = 0; i < vht_size; i++)
1183 	{
1184 	  corrupted = 0;
1185 	  if (vht[i] != NULL && vht[i]->nlen < 2)
1186 	    fprintf (stderr, "Short length %ld ", i);
1187 	  if (vht[i] !=NULL && vht[i]->nlen > 1)
1188 	    {
1189 	      if (vht[i]->nametxt[vht[i]->nstart] != ':')
1190 		{
1191 		  fprintf (stderr, "Ztart corrupted ");
1192 		  corrupted = 1;
1193 		};
1194 	      if (vht[i]->nametxt[vht[i]->nstart + vht[i]->nlen - 1] != ':')
1195 		{
1196 		  fprintf (stderr, "Zend corrupted ");
1197 		  corrupted = 1;
1198 		};
1199 	      if (corrupted)
1200 		{
1201 		  fprintf (stderr, " i %ld len %ld name = -",
1202 			   i, vht[i]->nlen );
1203 		  for (j = 0; j < vht[i]->nlen; j++)
1204 		    fprintf (stderr, "%c",
1205 			     vht[i]->nametxt[vht[i]->nstart + j]);
1206 		  fprintf (stderr, "- ");
1207 		}
1208 	    };
1209 	}
1210     };
1211 
1212 
1213   crm_nextword ( vname, vlen, 0, &vsidx, &vslen);
1214   if (internal_trace)
1215     {
1216       fprintf (stderr, " variable len %ld, name is -", vslen);
1217       for (k = vsidx; k < vsidx+vslen; k++)
1218 	fprintf (stderr, "%c", vname[k]);
1219 
1220       fprintf (stderr, "- .\n");
1221     };
1222 
1223   hc = (strnhash ( &vname[vsidx], vslen)) % vht_size;
1224 
1225   //  go exploring - find either an empty cell (meaning that this
1226   //  is the first time this variable name has been entered into the
1227   //  vht) or find the variable already entered.  Or find that we've
1228   //  gone the whole way 'round the vht, in which case the vht is full
1229   //  and we should print ut a message and fatal error away (or maybe
1230   //  even build a bigger vht?)
1231 
1232   i = hc;
1233 
1234   //   consider a "wrap" to have occurred if we even think about
1235   //   the slot just before the hashcoded slot
1236 
1237   done = 0;
1238   while ( ! done )
1239     {
1240       //  is there anything here yet?
1241       if (vht[i] == NULL)
1242 	{
1243 	  if (internal_trace)
1244 	    {
1245 	      int ic;
1246 	      fprintf (stderr, "  var ");
1247 	      for (ic = 0; ic < vlen; ic++)
1248 		fprintf (stderr, "%c", vname[ic]);
1249 	      fprintf (stderr, "(len %ld) not at %ld (empty)\n", vlen, i);
1250 	      fprintf (stderr, "Returning the index where it belonged.\n");
1251 	    };
1252 	  return (i);
1253 	};
1254 
1255       //  there's something here - is it what we have been seeking
1256       if ( vlen == vht[i]->nlen &&
1257 	   memcmp  (&((vht[i]->nametxt)[vht[i]->nstart]),
1258 		   vname,
1259 		   vlen) == 0)
1260 	{    //  Yes, we found it.
1261 	  if (internal_trace)
1262 	  {
1263 	    int ic;
1264 	    fprintf (stderr, "  var '");
1265 	    for (ic = 0; ic < vht[i]->nlen; ic++)
1266 	      fprintf (stderr, "%c", (vht[i]->nametxt)[ic+vht[i]->nstart] );
1267 	    fprintf (stderr, " (len %ld) found at %ld (",
1268 		     vlen, i);
1269 	    if (vht[i]->valtxt == cdw->filetext)
1270 	      { fprintf (stderr, "(main)"); }
1271 	    else
1272 	      { fprintf (stderr, "(isol)"); };
1273 	    fprintf (stderr, " s: %ld, l:%ld)\n",
1274 		     vht[i]->vstart, vht[i]->vlen);
1275 	  };
1276 	  return (i);
1277 	}
1278       else
1279 	{
1280 	  if (internal_trace)
1281 	    {
1282 	      int ic;
1283 	      fprintf (stderr, "\n Hash clash (at %ld): wanted %s (len %ld)",
1284 		       i, vname, vlen);
1285 	      fprintf (stderr, " but found '");
1286 	      for (ic = 0; ic < vht[i]->nlen; ic++)
1287 		fprintf (stderr, "%c", (vht[i]->nametxt)[ic+vht[i]->nstart] );
1288 	      fprintf (stderr, "' instead.");
1289 	    };
1290 	};
1291 
1292       i++;
1293       //  check wraparound
1294       if (i >= vht_size)
1295 	i = 0;
1296 
1297       //   check for hash table full - if it is, right now we
1298       //   do a fatal error.  Eventually we should just resize the
1299 	  //   hash table.  Even better- we should keep track of the number
1300       //   of variables, and thereby resize automatically whenever we
1301       //   get close to overflow.
1302       if (i == (hc - 1))
1303 	{
1304 	  static char badvarname [MAX_VARNAME];
1305 	  strncpy (badvarname, &vname[vsidx], vslen);
1306 	  badvarname[vslen+1] = 0;
1307 	  {
1308 	    long index;
1309 	    fprintf (stderr, "Variable Hash Table Dump\n");
1310 	    for (index = 0; index < vht_size; index++)
1311 	      {
1312 		int ic;
1313 		fprintf (stderr, "  var '");
1314 		for (ic = 0; ic < vht[index]->nlen; ic++)
1315 		  fprintf (stderr, "%c",
1316 			   (vht[index]->nametxt) [ic+vht[index]->nstart] );
1317 		fprintf (stderr, "'[%ld] found at %ld (",
1318 			 vht[index]->nlen,  index);
1319 		if (vht[index]->valtxt == cdw->filetext)
1320 		  { fprintf (stderr, "(main)"); }
1321 		else
1322 		  { fprintf (stderr, "(isol)"); };
1323 		fprintf (stderr, " s: %ld, l:%ld)\n",
1324 		     vht[index]->vstart, vht[index]->vlen);
1325 		; }
1326 	  };
1327 	  fatalerror5 (" Variable hash table overflow while looking "
1328 		      "for variable: " ,
1329 		       badvarname, CRM_ENGINE_HERE);
1330 	  done = 1;
1331 	  return (0);
1332 	};
1333     };
1334   return (0);
1335 }
1336 
1337 
1338 //
1339 //    crm_setvar - set the value of a variable into the VHT, putting a
1340 //    new cell in if necessary.  Note that this ONLY modifies the VHT
1341 //    data itself.  It does NOT do any of the background work like
1342 //    copying data at all, copying varnames into the tdw, keeping track
1343 //    of the cdw and tdw usage, etc.
1344 //
crm_setvar(char * filename,int filedesc,char * nametxt,long nstart,long nlen,char * valtxt,long vstart,long vlen,long linenumber,long lazy_redirects)1345 void crm_setvar (char *filename,
1346 		 int filedesc,
1347 		 char *nametxt,
1348 		 long nstart,
1349 		 long nlen,
1350 		 char *valtxt,
1351 		 long vstart,
1352 		 long vlen,
1353 		 long linenumber,
1354 		 long lazy_redirects)
1355 {
1356   int i, j;     // some indices to bang on
1357 
1358   //  first off, see if the variable is already stored.
1359 
1360   i = crm_vht_lookup (vht, &(nametxt[nstart]), nlen);
1361 
1362 
1363   if (vht[i] == NULL)
1364     {
1365       //    Nope, this is an empty VHT slot
1366 
1367       //  allocate a fresh, empty VHT cell
1368       vht[i] = (VHT_CELL *) malloc (sizeof (VHT_CELL));
1369       if (!vht[i])
1370 	untrappableerror5("Couldn't malloc space for VHT cell.  We need VHT cells for variables.  We can't continue.","", CRM_ENGINE_HERE);
1371 
1372       //  fill in the name info data
1373       vht[i]->filename = filename;
1374       vht[i]->filedesc = filedesc;
1375       vht[i]->nametxt = nametxt;
1376       vht[i]->nstart = nstart;
1377       vht[i]->nlen = nlen;
1378       vht[i]->vstart = 0 ;
1379       vht[i]->vlen = 0;\
1380       vht[i]->lazy_redirects = lazy_redirects;
1381 
1382       //  and now that the slot has proper initial information,
1383       //  we can use the same code as is used in an update to do
1384       //  the initial setting of values.  This is good because
1385       //  if we someday change the way variable values are stored,
1386       //  we need change it only in one place.
1387     }
1388   else
1389     {
1390       //   The cell is already here.  :)
1391     };
1392   //   Either way, the cell is now here, so we can set the value.
1393   //
1394   vht[i]->valtxt = valtxt;
1395   vht[i]->vstart = vstart;
1396   vht[i]->vlen = vlen;
1397   vht[i]->mstart = vstart;
1398   vht[i]->mlen = 0;
1399   vht[i]->linenumber = linenumber;
1400   vht[i]->lazy_redirects = lazy_redirects;
1401 
1402   if(internal_trace)
1403     {
1404       j = 0;
1405       fprintf (stderr, "  Successful set value of ");
1406 
1407       //for (j = 0; j < vht[i]->nlen; j++)
1408       //	fprintf (stderr, "%c", vht[i]->nametxt[vht[i]->nstart+j]);
1409       dontcare = fwrite (&(vht[i]->nametxt[vht[i]->nstart]), vht[i]->nlen, 1, stderr);
1410 
1411       fprintf (stderr, " at vht entry %d ", i);
1412 
1413       fprintf (stderr, " with value -");
1414       //      for (j = 0; j < vht[i]->vlen; j++)
1415       //	fprintf (stderr, "%c", vht[i]->valtxt[vht[i]->vstart+j]);
1416       dontcare = fwrite (&(vht[i]->valtxt[vht[i]->vstart]), vht[i]->vlen, 1, stderr);
1417 
1418       fprintf (stderr, "- (start %ld, length %ld)",
1419 	       vht[i]->vstart, vht[i]->vlen);
1420 
1421       fprintf (stderr, "and %ld lazy redirects", vht[i]->lazy_redirects);
1422 
1423       fprintf (stderr, "\n");
1424     };
1425 
1426 }
1427 
1428 //  look up what the line number is of a variable.
1429 //
crm_lookupvarline(VHT_CELL ** vht,char * text,long start,long len)1430 long crm_lookupvarline (VHT_CELL **vht, char *text, long start, long len)
1431 {
1432   int i;     // some indices to bang on
1433 
1434   i = crm_vht_lookup (vht, &(text[start]), len);
1435 
1436 
1437   //    GROT GROT GROT
1438   //    We should check here for GOTOing a label that isn't in
1439   //    the current file (i.e. the equivalent of a C "longjmp").
1440   if (vht[i] != NULL)
1441 	{           // Yes, we found it.  Return the line number
1442 	  if (internal_trace)
1443 	    fprintf (stderr, "  looked up ... line number %ld\n",
1444 		     vht[i]->linenumber);
1445 	  return (vht[i]->linenumber);
1446 	}
1447   else
1448     {
1449       //      long q;
1450       //      char *deathfu ;
1451       //      deathfu = (char *) malloc ( len+10);
1452       //      if (!deathfu)
1453       //	untrappableerror("Couldn't malloc 'deathfu'.\n  Time to die. ","");
1454       //      strncpy (deathfu, &(csl->filetext[start]), len);
1455       //      q = fatalerror ("Control Referencinge a non-existent variable- this"
1456       //		      "is almost always a very _bad_ thing",
1457       //		      deathfu);
1458       //  If fatalerror found a TRAP for this error, cstmt now points to
1459       //  the TRAP - 1.  We want to go to the trap itself, no auto-incr...
1460       //      if ( q == 0)
1461       //	return ( csl->cstmt + 1);
1462     };
1463   return (-1);
1464 }
1465 
1466 //      Update the start and length of all captured variables whenever
1467 //      a buffer gets mangled.  Mangles are all expressed in
1468 //      the form of a start point and a delta.
1469 //
1470 //     Note to the Reader - yes, I consider the nonlinearity of this
1471 //     function to be a grossitude.  Not quite an obscenity, but definitely
1472 //     a wart.
1473 
crm_updatecaptures(char * text,long loc,long delta)1474 void crm_updatecaptures (char *text, long loc, long delta)
1475 {
1476   long vht_index;
1477   long i;
1478   long ostart = 0, oend = 0 ;
1479   long nstart = 0, nend = 0 ;
1480 
1481   if (internal_trace)
1482     fprintf (stderr, "\n    updating captured values start %ld len %ld \n",
1483 	     loc, delta);
1484 
1485   //        check each VHT entry for a need to relocate
1486   for (vht_index = 0; vht_index < vht_size; vht_index++)
1487     {
1488       //      is this an actual entry?
1489       if (vht[vht_index] != NULL)
1490 	{
1491 	  if (vht[vht_index]->valtxt == text)
1492 	    {  // start of valtext block check
1493 	      // value text area
1494 	      if (internal_trace > 1)
1495 		{
1496 		  fprintf (stderr, "\n  checking var ");
1497 		  for (i = 0; i < vht[vht_index]->nlen; i++)
1498 		    fprintf (stderr, "%c",
1499 			vht[vht_index]->nametxt[vht[vht_index]->nstart+i]);
1500 		  fprintf (stderr, " ");
1501 		  fprintf (stderr, " s: %ld, l:%ld, e:%ld n:%ld ~ %ld ...",
1502 			   vht[vht_index]->vstart,
1503 			   vht[vht_index]->vlen,
1504 			   vht[vht_index]->vstart+vht[vht_index]->vlen,
1505 			   vht[vht_index]->nstart,
1506 			   vht[vht_index]->nstart + vht[vht_index]->nlen
1507 			   );
1508 		};
1509 	      ostart = nstart = vht[vht_index]->vstart;
1510 	      oend = nstart = ostart + vht[vht_index]->vlen;
1511 	      nstart = crm_mangle_offset (ostart, loc, delta, 0);
1512 	      nend = crm_mangle_offset (oend, loc, delta, 1);
1513 	      if (internal_trace)
1514 		fprintf (stderr, "\n   index %ld vstart/vlen upd: %ld, %ld  ",
1515 			 vht_index,
1516 			 vht[vht_index]->vstart, vht[vht_index]->vlen);
1517 	      vht[vht_index]->vstart = nstart;
1518 	      vht[vht_index]->vlen = nend - nstart;
1519 	      if (internal_trace)
1520 		fprintf (stderr, "to %ld, %ld.\n",
1521 			 vht[vht_index]->vstart,
1522 			 vht[vht_index]->vlen);
1523 	      //
1524 	      //        And do the same for mstart/mlen (match start/length)
1525 	      ostart = vht[vht_index]->mstart;
1526 	      oend = ostart + vht[vht_index]->mlen;
1527 	      nstart = crm_mangle_offset (ostart, loc, delta, 0);
1528 	      nend = crm_mangle_offset (oend, loc, delta, 1);
1529 	      if (internal_trace)
1530 		fprintf (stderr, "\n index %ld mstart/mlen upd: %ld, %ld  ",
1531 			 vht_index,
1532 			 vht[vht_index]->mstart, vht[vht_index]->mlen);
1533 	      vht[vht_index]->mstart = nstart;
1534 	      vht[vht_index]->mlen = nend - nstart;
1535 	      if (internal_trace)
1536 		fprintf (stderr, "to %ld, %ld.\n",
1537 			 vht[vht_index]->mstart,
1538 			 vht[vht_index]->mlen);
1539 	    };
1540 	  //      Don't forget entries that may be varNAMES, not just
1541 	  //      var values!
1542 	  if (vht[vht_index]->nametxt == text)
1543 	    {
1544 	      long orig_len;
1545 	      //
1546 	      //    Same thing here...
1547 	      //
1548 	      ostart = nstart = vht[vht_index]->nstart;
1549 	      orig_len = vht[vht_index]->nlen;
1550 	      oend = nend = ostart + orig_len;
1551 	      if (orig_len == 0) fprintf (stderr, "CRUD on %ld", vht_index);
1552 	      nstart = crm_mangle_offset (ostart, loc, delta, 0);
1553 	      nend = crm_mangle_offset (oend, loc, delta, 1);
1554 	      if (oend - ostart != orig_len)
1555 		fprintf (stderr, "Length change on %ld!  Was %ld, now %ld ",
1556 			 vht_index,
1557 			 orig_len,
1558 			 oend-ostart);
1559 
1560 	      if (internal_trace)
1561 		fprintf (stderr,
1562 			 "\n      index %ld nstart/nlen upd: %ld, %ld  ",
1563 			 vht_index,
1564 			 vht[vht_index]->nstart, vht[vht_index]->nlen);
1565 	      vht[vht_index]->nstart = nstart;
1566 	      vht[vht_index]->nlen = nend - nstart;
1567 	      if (internal_trace)
1568 		fprintf (stderr, "to %ld, %ld.\n",
1569 			 vht[vht_index]->nstart,
1570 			 vht[vht_index]->nlen);
1571 	    }
1572 	}
1573     }
1574       if (internal_trace)
1575 	fprintf (stderr, "\n    end of updates\n");
1576 }
1577 
1578 //
1579 //      How to calculate the new offsets of the start and end
1580 //      (that is, a "mark"), given a location (dot) and a delta of that
1581 //      location.  Dot doesn't move... only mark does.
1582 //
1583 //      sl is Start v. End - do we treat this mangle as altering the
1584 //      _start_ of a var, or the _end_ ?  (this is because we don't move
1585 //      a Start if Dot is the same, but we do move an End.  Alternatively,
1586 //      this is "is "dot considered to be before or after a mark with the
1587 //      same value)
1588 //
1589 
crm_mangle_offset(long mark,long dot,long delta,long sl)1590 long crm_mangle_offset ( long mark, long dot, long delta, long sl)
1591 {
1592   long absdelta;
1593 
1594   absdelta = delta;
1595   if (absdelta < 0) absdelta = -absdelta;
1596 
1597   if (sl == 0)
1598     {
1599       //     HOW WE DEAL WITH START POINTS
1600       //     (that is, "dot" is considered to follow "mark")
1601       //
1602       //   are we earlier than dot?  If so, we can't be changed by dot.
1603       //
1604       // edge condition for start:
1605       //
1606       //         Mark   ==>     Mark
1607       //         Dot             Dot
1608       //
1609       if (mark <= dot) return (mark);
1610 
1611       //   are we beyond the reach of dot and delta?  If so, we just slide.
1612       //
1613       // edge condition:
1614       //
1615       //         Mark   ==>        Mark
1616       //         Dot+Delta        Dot
1617       //
1618 
1619       if ((dot + absdelta) < mark ) return (mark + delta);
1620 
1621       //   Neither - we're in the range where dot and mark can affect us
1622       //
1623       //   If delta is positive, we can just slide further out.
1624       if (delta > 0) return (mark + delta);
1625       //
1626       //   but, if delta is negative (a deletion) then we can move toward
1627       //   dot, but not earlier than dot.
1628       mark = mark + delta; //  delta is negative, so we ADD it to subtract!
1629       if (mark < dot) mark = dot;
1630       return (mark);
1631     }
1632   else
1633     {
1634       //     HOW WE DEAL WITH END POINTS
1635       //     (that is, "dot" is considered to be in front of "mark")
1636       //
1637       //   are we earlier than dot?  If so, we can't be changed by dot.
1638       //
1639       // edge condition for finish points:
1640       //
1641       //         Mark   ==>     Mark
1642       //         Dot           Dot
1643       //
1644       if (mark < dot) return (mark);
1645 
1646       //   are we beyond the reach of dot and delta?  If so, we just slide.
1647       //
1648       // edge condition:
1649       //
1650       //         Mark   ==>        Mark
1651       //         Dot+Delta          Dot
1652       //
1653 
1654       if ((dot + absdelta) <= mark ) return (mark + delta);
1655 
1656       //   Neither - we're in the range where dot and mark can affect us
1657       //
1658       //   If delta is positive, we can just slide further out.
1659       if (delta > 0) return (mark + delta);
1660       //
1661       //   but, if delta is negative (a deletion) then we can move toward
1662       //   dot, but not earlier than dot.
1663       mark = mark + delta; //  delta is negative, so we ADD it to subtract!
1664       if (mark < dot) mark = dot;
1665       return (mark);
1666     };
1667 }
1668 
1669 ///
1670 //
1671 //     crm_buffer_gc - garbage-collect a buffer.  This isn't a perfect
1672 //     solution, but it will work.  (i.e. it's slow and annoying)//
1673 //
1674 //     The algorithm:
1675 //      - find the lowest index currently used (takes 1 pass thru VHT)
1676 //      - find the highest user of that index (takes 1 pass thru VHT)
1677 //        * - see if any block overlaps that block
1678 //      - find the next lowest starting block
1679 //
1680 
crm_buffer_gc(CSL_CELL * zdw)1681 int crm_buffer_gc ( CSL_CELL *zdw)
1682 {
1683   fprintf (stderr, "Sorry, GC is not yet implemented");
1684   exit (EXIT_FAILURE);
1685   return (0);
1686 }
1687