1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1984-2013 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * make state variable routines
26 * this file and the is*() macros specify the state variable name format
27 *
28 * (<var>) the state of <var>
29 * (<var>)<rule> the state of <var> qualified by <rule>
30 * ()<rule> the state of <rule>
31 *
32 * NOTE: update VERSION in compile.c if the format changes
33 */
34
35 #include "make.h"
36 #include "options.h"
37
38 /*
39 * return a pointer to the rule <s1><s2><s3>
40 * force causes the rule to be created
41 * force<0 canon's name first
42 */
43
44 Rule_t*
catrule(register char * s1,register char * s2,register char * s3,int force)45 catrule(register char* s1, register char* s2, register char* s3, int force)
46 {
47 Rule_t* r;
48
49 sfputr(internal.nam, s1, *s2++);
50 if (*s2)
51 sfputr(internal.nam, s2, s3 ? *s3++ : -1);
52 if (s3 && *s3)
53 sfputr(internal.nam, s3, -1);
54 s1 = sfstruse(internal.nam);
55 if (!(r = getrule(s1)) && force)
56 {
57 if (force < 0)
58 {
59 pathcanon(s1, 0, 0);
60 if (r = getrule(s1))
61 return r;
62 }
63 r = makerule(NiL);
64 }
65 return r;
66 }
67
68 /*
69 * return the state file name
70 * assumes the main makefile has already been read
71 */
72
73 char*
statefile(void)74 statefile(void)
75 {
76 char* dir;
77 Sfio_t* sp;
78 Stat_t st;
79
80 if (!state.statefile && state.makefile)
81 {
82 sp = sfstropen();
83 dir = DELETE;
84 if (!state.writestate || streq(state.writestate, "-") || !stat(state.writestate, &st) && S_ISDIR(st.st_mode) && (dir = state.writestate))
85 edit(sp, state.makefile, dir, KEEP, external.state);
86 else
87 expand(sp, state.writestate);
88 state.statefile = strdup(sfstruse(sp));
89 sfstrclose(sp);
90 }
91 return state.statefile;
92 }
93
94 /*
95 * reconcile s with state view from r
96 *
97 * NOTE: requires state.maxview>0 && 0<=view<=state.maxview
98 */
99
100 static Rule_t*
stateview(int op,char * name,register Rule_t * s,register Rule_t * r,int view,int accept,Rule_t ** pv)101 stateview(int op, char* name, register Rule_t* s, register Rule_t* r, int view, int accept, Rule_t** pv)
102 {
103 register Rule_t* v;
104 register List_t* p;
105 Sfio_t* fp;
106
107 if (pv)
108 *pv = 0;
109 if (state.compile < COMPILED)
110 {
111 #if DEBUG
112 if (state.test & 0x00000200)
113 error(2, "STATEVIEW %d %s state file load delayed until after makefile read", view, name);
114 #endif
115 return 0;
116 }
117 if (!(state.view[view].flags & BIND_LOADED))
118 {
119 if (view < state.readstate)
120 {
121 char* file;
122 Sfio_t* tmp;
123 long n;
124
125 tmp = sfstropen();
126 if (name && !s)
127 {
128 sfputr(tmp, name, 0);
129 n = sfstrtell(tmp);
130 }
131 else
132 n = 0;
133 edit(tmp, statefile(), state.view[view].path, KEEP, KEEP);
134 sfputc(tmp, 0);
135 file = sfstrseek(tmp, n, SEEK_SET);
136 if (fp = sfopen(NiL, file, "br"))
137 {
138 /*
139 * NOTE: this load should not be a problem for
140 * internal rule pointers since all
141 * non-state rules in state files
142 * are just references
143 */
144
145 state.stateview = view;
146 message((-2, "loading state view %d file %s", view, file));
147 if (load(fp, file, 0, 0) > 0)
148 state.view[view].flags |= BIND_EXISTS;
149 else if (state.corrupt && *state.corrupt == 'a' && !(state.view[0].flags & BIND_EXISTS))
150 state.accept = 1;
151 state.stateview = -1;
152 sfclose(fp);
153 }
154 if (n)
155 strcpy(name, sfstrbase(tmp));
156 sfstrclose(tmp);
157 }
158 state.view[view].flags |= BIND_LOADED;
159 }
160 if (name)
161 {
162 viewname(name, view);
163 v = getrule(name);
164 unviewname(name);
165 #if DEBUG
166 if (state.test & 0x00000200)
167 error(2, "STATEVIEW %s [%s] test %d [%s] -> %s [%s]", name, s ? timestr(s->time) : "no rule", view, timestr(r->time), v ? v->name : null, v ? timestr(v->time) : "no rule");
168 #endif
169 if (v)
170 {
171 if (pv)
172 *pv = v;
173 if (s && (op == RULE && (s->event >= v->event && s->event || !v->time && (v->property & P_force)) || op == PREREQS && s->time >= v->time))
174 return s;
175 if (v->time == r->time || accept || r->view == view || (r->property & (P_state|P_use|P_virtual)) || state.believe && view >= (state.believe - 1))
176 {
177 if (r->property & P_state)
178 {
179 if (r->property & P_statevar)
180 {
181 if (r->statedata && (!v->statedata && *r->statedata || v->statedata && !streq(r->statedata, v->statedata) || r->time > v->time))
182 return 0;
183 s = r;
184 s->statedata = v->statedata;
185 if (v->property & P_parameter)
186 s->property |= P_parameter;
187 }
188 }
189 else
190 {
191 if (r->property & P_use)
192 {
193 if (r->action && (!v->action || !streq(r->action, v->action)) || !r->action && v->action)
194 return 0;
195 r->time = v->time;
196 }
197 if (!s)
198 s = makerule(name);
199 }
200 s->time = v->time;
201 s->attribute = v->attribute;
202 s->event = v->event;
203 s->action = v->action;
204 if (v->property & P_force)
205 s->property |= P_force;
206 else
207 s->property &= ~P_force;
208 if (v->dynamic & D_built)
209 s->dynamic |= D_built;
210 else
211 s->dynamic &= ~D_built;
212 if (v->dynamic & D_lowres)
213 s->dynamic |= D_lowres;
214 else
215 s->dynamic &= ~D_lowres;
216 s->prereqs = listcopy(v->prereqs);
217 s->scan = v->scan;
218 for (p = s->prereqs; p; p = p->next)
219 {
220 v = p->rule;
221 if (v->dynamic & D_lower)
222 {
223 unviewname(v->name);
224 p->rule = makerule(v->name);
225 viewname(v->name, v->view);
226 }
227 }
228 #if DEBUG
229 if (state.test & 0x00000200)
230 {
231 error(2, "STATEVIEW %s accept %d", s->name, view);
232 if (state.test & 0x00000400)
233 dumprule(sfstderr, s);
234 }
235 #endif
236 }
237 }
238 }
239 return s;
240 }
241
242 /*
243 * return a pointer to the state rule of var qualified by r
244 * force>0 causes the state rule to be created
245 * force<0 prevents a state bind
246 */
247
248 Rule_t*
staterule(int op,register Rule_t * r,char * var,int force)249 staterule(int op, register Rule_t* r, char* var, int force)
250 {
251 register Rule_t* s;
252 register int i;
253 char* rul;
254 char* nam;
255 Rule_t* v;
256 int j;
257 int k;
258 int m;
259 int nobind = force < 0;
260 Flags_t* b;
261
262 switch (op)
263 {
264 case PREREQS:
265 rul = var;
266 var = "+";
267 if (!r)
268 {
269 if (nobind)
270 {
271 sfprintf(internal.nam, "(%s)%s", var, rul);
272 r = getrule(sfstruse(internal.nam));
273 }
274 return r;
275 }
276 break;
277 case RULE:
278 rul = var;
279 var = null;
280 if (!r)
281 {
282 if (nobind)
283 {
284 sfprintf(internal.nam, "(%s)%s", var, rul);
285 return getrule(sfstruse(internal.nam));
286 }
287 r = makerule(var);
288 nobind = 1;
289 }
290 break;
291 case VAR:
292 if (!r)
293 {
294 sfprintf(internal.nam, "(%s)", var);
295 if (!(r = getrule(sfstruse(internal.nam))))
296 {
297 if (!force)
298 return 0;
299 r = makerule(sfstrbase(internal.nam));
300 }
301 }
302 if (nobind)
303 return r;
304 break;
305 #if DEBUG
306 default:
307 error(PANIC, "invalid op=%d to staterule()", op);
308 break;
309 #endif
310 }
311 again:
312 if (r->property & P_statevar)
313 {
314 if (op == PREREQS)
315 return 0;
316 op = RULE;
317 nam = r->name;
318 s = r;
319 }
320 else if (r->property & P_state)
321 return 0;
322 else
323 {
324 if ((r->dynamic & (D_member|D_membertoo)) && (rul = strrchr(r->name, '/')))
325 rul++;
326 else
327 rul = unbound(r);
328 sfprintf(internal.nam, "(%s)%s", var, rul);
329 nam = sfstruse(internal.nam);
330 if (s = getrule(nam))
331 nam = s->name;
332 }
333 if (state.maxview && state.readstate && state.makefile && !nobind)
334 {
335 b = &r->checked[op];
336 k = 0;
337 if (!tstbit(*b, i = (r->property & (P_statevar|P_use|P_virtual)) && state.targetview > 0 ? state.targetview : r->view) && (!s || !s->time || (r->property & P_statevar) || !(k = statetimeq(r, s))))
338 {
339 if (!(r->property & (P_statevar|P_use|P_virtual)) && !(r->dynamic & D_bound) && !(r->mark & M_bind) && (s && s->time || !s && state.compile >= COMPILED))
340 {
341 /*
342 * M_bind guards staterule() recursion
343 */
344
345 s = r;
346 s->mark |= M_bind;
347 r = bind(r);
348 s->mark &= ~M_bind;
349 goto again;
350 }
351 if ((r->property & P_statevar) && r->time == OLDTIME)
352 {
353 for (i = 0; i <= state.maxview; i++)
354 {
355 setbit(*b, i);
356 setbit(r->checked[CONSISTENT], i);
357 }
358 }
359 else
360 {
361 if (!(r->property & (P_statevar|P_use|P_virtual)) || state.targetview < 0)
362 {
363 m = 1;
364 j = 0;
365 if (!(k = r->view) && !(state.view[0].flags & BIND_EXISTS))
366 k = state.maxview;
367 }
368 else
369 {
370 m = 0;
371 j = k = state.targetview;
372 }
373 #if DEBUG
374 if (state.test & 0x00000200)
375 error(2, "STATERULE %s search %d..%d targetview=%d", nam, j, k, state.targetview);
376 #endif
377 for (i = j; i <= k; i++)
378 {
379 setbit(*b, i);
380 if (i || !(r->property & P_statevar))
381 {
382 s = stateview(op, nam, s, r, i, i == k, &v);
383 if (v)
384 {
385 if (m)
386 {
387 m = i;
388 while (++m <= k)
389 setbit(*b, m);
390 }
391 if (op != PREREQS || state.accept)
392 do setbit(r->checked[CONSISTENT], i); while (--i > j);
393 break;
394 }
395 }
396 if (state.accept)
397 setbit(r->checked[CONSISTENT], i);
398 if (!i)
399 {
400 if (op != PREREQS)
401 setbit(r->checked[CONSISTENT], i);
402 k = state.maxview;
403 }
404 }
405 }
406 }
407 else if (k)
408 setbit(*b, i);
409 }
410 if (!s && force > 0)
411 s = makerule(nam);
412 if (s && op == RULE)
413 {
414 r->attribute |= s->attribute & internal.retain->attribute;
415 r->property |= s->property & (P_dontcare|P_terminal);
416 }
417 return s;
418 }
419
420 /*
421 * return a non-state rule pointer corresponding to the staterule r
422 * force causes the non-state rule to be created
423 */
424
425 Rule_t*
rulestate(register Rule_t * r,int force)426 rulestate(register Rule_t* r, int force)
427 {
428 register char* s;
429
430 if (r->property & P_staterule)
431 {
432 s = r->name;
433 while (*s && *s++ != ')');
434 if (!(r = getrule(s)) && force)
435 r = makerule(s);
436 }
437 return r;
438 }
439
440 /*
441 * return a variable pointer corresponding to the state variable r
442 * force causes the variable to be created
443 */
444
445 Var_t*
varstate(register Rule_t * r,int force)446 varstate(register Rule_t* r, int force)
447 {
448 register char* s;
449 register char* t;
450 register Var_t* v;
451
452 s = r->name;
453 if (r->property & P_state)
454 {
455 if (r->property & P_statevar)
456 {
457 s++;
458 *(t = s + strlen(s) - 1) = 0;
459 }
460 else
461 return 0;
462 }
463 else
464 t = 0;
465 if (!(v = getvar(s)) && force)
466 v = setvar(s, null, force < 0 ? V_retain : 0);
467 if (t)
468 *t = ')';
469 return v;
470 }
471
472 /*
473 * return the auxiliary variable pointer for s
474 * force causes the variable to be created
475 */
476
477 Var_t*
auxiliary(char * s,int force)478 auxiliary(char* s, int force)
479 {
480 Var_t* v;
481
482 sfprintf(internal.nam, "(&)%s", s);
483 if (!(v = getvar(sfstruse(internal.nam))) && force)
484 v = setvar(sfstrbase(internal.nam), null, 0);
485 return v;
486 }
487
488 /*
489 * force r->scan == (*(unsigned char*)h) or all files to be re-scanned
490 */
491
492 int
forcescan(const char * s,char * v,void * h)493 forcescan(const char* s, char* v, void* h)
494 {
495 register Rule_t* r = (Rule_t*)v;
496 register int n = h ? *((unsigned char*)h) : r->scan;
497
498 NoP(s);
499 if ((r->property & P_staterule) && r->scan == n && !(r->dynamic & D_scanned))
500 r->property |= P_force;
501 return 0;
502 }
503
504 /*
505 * report state file lock fatal error
506 */
507
508 static void
badlock(char * file,int view,Time_t date)509 badlock(char* file, int view, Time_t date)
510 {
511 long d;
512
513 /*
514 * probably a bad lock if too old
515 */
516
517 d = state.regress ? 0 : (CURSECS - tmxsec(date));
518 if (d > 24 * 60 * 60)
519 error(1, "%s is probably an invalid lock file", file);
520 else if (d > 0)
521 error(1, "another make has been running on %s in %s for the past %s", state.makefile, state.view[view].path, fmtelapsed(d, 1));
522 else
523 error(1, "another make is running on %s in %s", state.makefile, state.view[view].path);
524 error(3, "use -%c to override", OPT(OPT_ignorelock));
525 }
526
527 /*
528 * get|release exclusive (advisory) access to state file
529 *
530 * this is a general solution that should work on all systems
531 * the following problems need to be addressed
532 *
533 * o flock() or lockf() need to work for distributed
534 * as well as local file systems
535 *
536 * o the file system clock may be different than the
537 * local system clock
538 *
539 * o creating a specific lock file opens the door for
540 * lock files laying around after program and system
541 * crashes -- placing the pid of the locking process
542 * as file data may not work on distributed process
543 * systems
544 */
545
546 #define LOCKTIME(p,m) ((m)?tmxgetmtime(p):tmxgetctime(p))
547
548 void
lockstate(int set)549 lockstate(int set)
550 {
551 register int fd;
552 register char* file;
553 Time_t t;
554 Stat_t st;
555
556 static char* lockfile;
557 static Time_t locktime;
558 static int lockmtime;
559
560 if (set)
561 {
562 if (!state.exec || state.virtualdot || !state.writestate)
563 return;
564 edit(internal.nam, statefile(), KEEP, KEEP, external.lock);
565 file = strdup(sfstruse(internal.nam));
566 if (!state.ignorelock)
567 {
568 int uid = geteuid();
569
570 for (fd = 1; fd <= state.maxview; fd++)
571 {
572 edit(internal.nam, file, state.view[fd].path, KEEP, KEEP);
573 if (!stat(sfstruse(internal.nam), &st) && st.st_uid != uid)
574 badlock(sfstrbase(internal.nam), fd, LOCKTIME(&st, lockmtime));
575 }
576 }
577 locktime = 0;
578 for (;;)
579 {
580 lockfile = file;
581 if ((fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY|O_CLOEXEC, 0)) >= 0)
582 break;
583 lockfile = 0;
584 if (stat(file, &st) < 0)
585 error(3, "cannot create lock file %s", file);
586 if (!state.ignorelock)
587 badlock(file, 0, LOCKTIME(&st, lockmtime));
588 if (remove(file) < 0)
589 error(3, "cannot remove lock file %s", file);
590 }
591
592 /*
593 * fstat() here would be best but some systems
594 * like cygwin which shall remain nameless
595 * return different time values after the close()
596 */
597
598 close(fd);
599 if (stat(file, &st) < 0)
600 error(3, "cannot stat lock file %s", file);
601 lockmtime = tmxgetatime(&st) < tmxgetmtime(&st) || tmxgetctime(&st) < tmxgetmtime(&st);
602 locktime = LOCKTIME(&st, lockmtime);
603 }
604 else if (lockfile)
605 {
606 if (locktime)
607 {
608 if (stat(lockfile, &st) < 0 || (t = LOCKTIME(&st, lockmtime)) != locktime && t != tmxsns(tmxsec(locktime), 0))
609 {
610 if (state.writestate)
611 error(1, "the state file lock on %s has been overridden", state.makefile);
612 }
613 else if (remove(lockfile) < 0)
614 error(1, "cannot remove lock file %s", lockfile);
615 }
616 else
617 remove(lockfile);
618 free(lockfile);
619 lockfile = 0;
620 }
621 }
622
623 /*
624 * read state from a previous make
625 */
626
627 void
readstate(void)628 readstate(void)
629 {
630 register Sfio_t* fp;
631 char* file;
632
633 if (state.makefile)
634 {
635 lockstate(1);
636 if (state.readstate && (fp = sfopen(NiL, file = statefile(), "br")))
637 {
638 state.stateview = 0;
639 message((-2, "loading state file %s", file));
640 makerule(file)->dynamic |= D_built;
641 if (load(fp, file, 0, 10) > 0)
642 state.view[0].flags |= BIND_EXISTS;
643 else if (!state.corrupt)
644 error(3, "use -%c%c to accept current state or -%c to remake", OPT(OPT_accept), OPT(OPT_readstate), OPT(OPT_readstate));
645 else if (*state.corrupt == 'a')
646 state.accept = 1;
647 state.stateview = -1;
648 sfclose(fp);
649 }
650 }
651 state.view[0].flags |= BIND_LOADED;
652 }
653
654 /*
655 * update the superimposed code
656 */
657
658 static void
code(register const char * s)659 code(register const char* s)
660 {
661 debug((-6, "enter candidate state variable %s", s));
662 settype(*s++, C_VARPOS1);
663 if (*s)
664 {
665 settype(*s++, C_VARPOS2);
666 if (*s)
667 {
668 settype(*s++, C_VARPOS3);
669 if (*s)
670 {
671 settype(*s++, C_VARPOS4);
672 if (*s)
673 {
674 settype(*s++, C_VARPOS5);
675 if (*s)
676 {
677 settype(*s++, C_VARPOS6);
678 if (*s)
679 {
680 settype(*s++, C_VARPOS7);
681 if (*s) settype(*s, C_VARPOS8);
682 }
683 }
684 }
685 }
686 }
687 }
688 }
689
690 /*
691 * bind() and scan() r->parameter file prerequisites
692 * this catches all implicit state variables before the
693 * parent files are scanned
694 */
695
696 static int
checkparam(const char * s,char * v,void * h)697 checkparam(const char* s, char* v, void* h)
698 {
699 register Rule_t* r = (Rule_t*)v;
700 register List_t* p;
701 register char* t;
702 Time_t tm;
703
704 NoP(s);
705 NoP(h);
706 if ((r->property & (P_attribute|P_parameter|P_state)) == P_parameter)
707 {
708 r->property |= P_ignore;
709 maketop(r, 0L, NiL);
710 for (p = scan(r, &tm); p; p = p->next)
711 if (((r = p->rule)->property & (P_parameter|P_statevar)) == (P_parameter|P_statevar))
712 {
713 r->dynamic |= D_scanned;
714 if (t = strchr(r->name, ')'))
715 {
716 *t = 0;
717 code(r->name + 1);
718 *t = ')';
719 }
720 }
721 }
722 return 0;
723 }
724
725 /*
726 * check implicit state variable vars
727 */
728
729 static int
checkvar1(register const char * s,char * u,void * h)730 checkvar1(register const char* s, char* u, void* h)
731 {
732 register Var_t* v = (Var_t*)u;
733 register Rule_t* r;
734
735 NoP(h);
736 if (v->property & V_scan)
737 {
738 state.fullscan = 1;
739 r = staterule(VAR, NiL, (char*)s, 1);
740 if (!r->scan)
741 {
742 debug((-5, "%s and %s force re-scan", v->name, r->name));
743 r->scan = SCAN_STATE;
744 state.forcescan = 1;
745 }
746 code(s);
747 }
748 return 0;
749 }
750
751 /*
752 * check implicit state variable rules
753 */
754
755 static int
checkvar2(const char * s,char * u,void * h)756 checkvar2(const char* s, char* u, void* h)
757 {
758 register Rule_t* r = (Rule_t*)u;
759 Var_t* v;
760
761 NoP(s);
762 NoP(h);
763 if ((r->property & P_statevar) && r->scan && !r->view && (!(v = varstate(r, 0)) || !(v->property & V_scan)) && (!(r->property & P_parameter) || !(r->dynamic & D_scanned)))
764 {
765 debug((-5, "%s forces re-scan", r->name));
766 r->scan = 0;
767 state.forcescan = 1;
768 }
769 return 0;
770 }
771
772 /*
773 * freeze the parameter files and candidate state variables
774 */
775
776 void
candidates(void)777 candidates(void)
778 {
779 int view;
780
781 if (state.scan)
782 {
783 message((-2, "freeze candidate state variables and parameter files"));
784 hashwalk(table.rule, 0, checkparam, NiL);
785 hashwalk(table.var, 0, checkvar1, NiL);
786 hashwalk(table.rule, 0, checkvar2, NiL);
787 if (state.forcescan)
788 {
789 for (view = 1; view <= state.maxview; view++)
790 stateview(0, NiL, NiL, NiL, view, 0, NiL);
791 hashwalk(table.rule, 0, forcescan, NiL);
792 }
793 }
794 }
795
796 /*
797 * save state for the next make
798 */
799
800 void
savestate(void)801 savestate(void)
802 {
803 char* file;
804
805 if (state.makefile && state.user && state.compile == COMPILED)
806 {
807 if (state.writestate)
808 {
809 if (state.finish)
810 state.compile = SAVED;
811 if (state.exec && state.savestate)
812 {
813 file = statefile();
814 message((-2, "saving state in %s", file));
815 state.stateview = 0;
816 compile(file, NiL);
817 state.stateview = -1;
818 state.savestate = 0;
819 }
820 }
821 if (state.finish)
822 lockstate(0);
823 }
824 }
825
826 /*
827 * bind statevar r to a variable
828 */
829
830 Rule_t*
bindstate(register Rule_t * r,register char * val)831 bindstate(register Rule_t* r, register char* val)
832 {
833 Rule_t* s;
834 Time_t t;
835
836 #if DEBUG
837 if (!(r->property & P_state))
838 error(PANIC, "bindstate(%s) called for non-state rule", r->name);
839 #endif
840 if (state.maxview && (r->property & P_statevar) && (s = staterule(VAR, r, NiL, 0)))
841 r = s;
842 if ((r->dynamic & D_bound) && !val)
843 return r;
844 if (r->property & P_statevar)
845 {
846 register Var_t* v;
847 char* e;
848 Sfio_t* tmp = 0;
849
850 /*
851 * determine the current state variable value
852 */
853
854 if (val)
855 r->property |= P_virtual;
856 else if (r->property & P_virtual)
857 val = null;
858 else if (v = varstate(r, 0))
859 {
860 tmp = sfstropen();
861 r->dynamic |= D_bound;
862 expand(tmp, getval(v->name, VAL_PRIMARY));
863 r->dynamic &= ~D_bound;
864 val = sfstruse(tmp);
865 }
866 else if ((r->property & P_parameter) && r->statedata)
867 val = r->statedata;
868 else if (*(r->name + 1) == '-')
869 {
870 *(e = r->name + strlen(r->name) - 1) = 0;
871 val = getval(r->name + 1, 0);
872 *e = ')';
873 }
874 else
875 val = null;
876 if (!r->time && state.maxview && (state.view[0].flags & BIND_LOADED))
877 {
878 /*
879 * see if some other view has an initial value
880 */
881
882 r->statedata = strdup(val);
883 staterule(RULE, r, NiL, 0);
884 }
885
886 /*
887 * check if the state variable value changed
888 * the previous value, if defined, has already
889 * been placed in r->statedata by readstate()
890 */
891
892 message((-2, "checking state variable %s value `%s'", r->name, r->statedata ? r->statedata : null));
893 if (!r->time || r->statedata && !streq(r->statedata, val) || !r->statedata && *val)
894 {
895 /*
896 * state variable changed
897 */
898
899 if (!r->view && !(r->property & P_accept))
900 {
901 if (r->time)
902 reason((1, "state variable %s changed to `%s' from `%s'", r->name, val, r->statedata));
903 else
904 reason((1, "state variable %s initialized to `%s'", r->name, val));
905 }
906 state.savestate = 1;
907 if (r->statedata != val)
908 r->statedata = strdup(val);
909 if (r->time == (t = CURTIME))
910 t++;
911 r->time = t;
912 }
913 if ((r->property & P_accept) || state.accept)
914 r->time = OLDTIME;
915 if (tmp)
916 sfstrclose(tmp);
917 }
918 bindattribute(r);
919 return r;
920 }
921
922 /*
923 * check and stat built target r
924 * otherwise check for motion from . to dir of r
925 *
926 * NOTE: this needs clarification
927 */
928
929 static int
checkcurrent(register Rule_t * r,Stat_t * st)930 checkcurrent(register Rule_t* r, Stat_t* st)
931 {
932 register int n;
933 register char* s;
934 long pos;
935
936 if (r->uname && !(n = rstat(r->uname, st, 1)))
937 oldname(r);
938 else if ((n = rstat(r->name, st, 1)) && (state.exec || state.mam.out && !state.mam.port))
939 {
940 rebind(r, -1);
941 n = rstat(r->name, st, 1);
942 }
943 if (!n && !(r->dynamic & D_entries) && S_ISREG(st->st_mode))
944 r->dynamic |= D_regular;
945 if (!(r->dynamic & D_triggered))
946 return n;
947 edit(internal.nam, r->name, KEEP, DELETE, DELETE);
948 if (!(pos = sfstrtell(internal.nam)))
949 return n;
950 sfputc(internal.nam, 0);
951 sfputr(internal.nam, r->name, 0);
952 s = sfstrseek(internal.nam, pos + 1, SEEK_SET);
953 pathcanon(s, 0, 0);
954 if (!streq(r->name, s))
955 {
956 if (!r->uname)
957 r->uname = r->name;
958 r->name = strdup(s);
959 }
960 s = sfstrseek(internal.nam, 0, SEEK_SET);
961 #if DEBUG
962 if (state.test & 0x00000100)
963 error(2, "statetime(%s): dir=%s n=%d time=[%s]", r->name, s, n, timestr(n ? NOTIME : tmxgetmtime(st)));
964 #endif
965 newfile(r, s, n ? NOTIME : tmxgetmtime(st));
966 return n;
967 }
968
969 /*
970 * update internal time of r after its action has completed
971 * sync>0 syncs the state rule prereqs and action
972 * sync<0 resolves r but does not update state
973 */
974
975 Time_t
statetime(register Rule_t * r,int sync)976 statetime(register Rule_t* r, int sync)
977 {
978 register Rule_t* s;
979 int a;
980 int n;
981 int skip = 0;
982 int zerostate = 0;
983 Time_t t;
984 Time_t q;
985 Rule_t* x;
986 Stat_t st;
987 Stat_t ln;
988
989 if (r->property & P_state)
990 {
991 if ((r->dynamic & D_triggered) && state.exec)
992 r->time = ((r->property & P_statevar) && r->status == FAILED) ? (Time_t)0 : CURTIME;
993 return r->time;
994 }
995 s = 0;
996 if (state.interrupt && r->status != EXISTS)
997 zerostate = 1;
998 else if (r->status == FAILED)
999 {
1000 r->time = 0;
1001 tmxsetmtime(&st, r->time);
1002 if ((state.test & 0x00040000) && (s = staterule(RULE, r, NiL, 0)))
1003 {
1004 r->time = s->time;
1005 if (r->property & (P_metarule|P_state))
1006 r->event = s->event;
1007 state.savestate = 1;
1008 skip = 1;
1009 }
1010 }
1011 else if (r->property & P_virtual)
1012 {
1013 r->time = CURTIME;
1014 tmxsetmtime(&st, r->time);
1015 }
1016 else if (checkcurrent(r, &st))
1017 {
1018 if (r->property & P_dontcare)
1019 t = 0;
1020 else
1021 {
1022 t = CURTIME;
1023 zerostate = 1;
1024 }
1025 tmxsetmtime(&st, t);
1026 }
1027 else if (sync < 0)
1028 return r->time;
1029 else if ((s = staterule(RULE, r, NiL, 1)) && s->time == tmxgetmtime(&st))
1030 {
1031 if (state.exec && !state.touch)
1032 {
1033 /*
1034 * this alternate event time prevents the action from
1035 * triggering next time if nothing else changes
1036 */
1037
1038 x = staterule(PREREQS, r, NiL, 1);
1039 x->dynamic &= ~D_lowres;
1040 x->time = r->time;
1041 r->time = s->event;
1042 if (r->dynamic & D_triggered)
1043 r->dynamic |= D_same;
1044 }
1045 state.savestate = 1;
1046 }
1047 else if ((r->dynamic & D_triggered) && state.exec)
1048 {
1049 static int localsync;
1050 static int localtest;
1051 static Time_t localskew;
1052
1053 /*
1054 * r is built since its time changed after its action triggered
1055 */
1056
1057 s->dynamic |= D_built;
1058 if (x = staterule(PREREQS, r, NiL, 0))
1059 x->property |= P_force;
1060
1061 /*
1062 * check for file system and local system time consistency
1063 * directories, archives and multi hard link files not sync'd
1064 */
1065
1066 if (st.st_nlink <= 1 && !S_ISDIR(st.st_mode) && !(r->property & P_archive))
1067 {
1068
1069 #if DEBUG
1070 if (state.test & 0x00000100)
1071 error(2, "%s: r[%s] s[%s] f[%s]", r->name, timestr(r->time), timestr(s->time), timestr(tmxgetmtime(&st)));
1072 #endif
1073 if (!localsync && !state.override && r->time && r->time != OLDTIME && !(r->property & P_force) && tmxgetmtime(&st) == tmxgetctime(&st))
1074 {
1075 if (((n = (tmxsec(r->time) - (unsigned long)st.st_mtime - 1)) >= 0 || (n = (CURSECS - (unsigned long)st.st_mtime + 2)) <= 0) && (lstat(r->name, &ln) || !S_ISLNK(ln.st_mode)))
1076 {
1077 /*
1078 * warn if difference not tolerable
1079 */
1080
1081 a = (n > 0) ? n : -n;
1082 if (a > 1)
1083 error(state.regress ? -1 : 1, "%s file system time %s local time by at least %s", r->name, n > 0 ? "lags" : "leads", fmtelapsed(a, 1));
1084 localsync = a > state.tolerance ? 1 : -1;
1085 }
1086 }
1087 if (localsync > 0)
1088 {
1089 /*
1090 * NOTE: time stamp syncs work on the assumption that
1091 * all source files have an mtime that is older
1092 * than CURTIME -- this isn't too bad since
1093 * only built files are sync'd; also note that
1094 * nsec is set to 0 to avoid resolution mismatches
1095 */
1096
1097 for (;;)
1098 {
1099 t = tmxsns(CURSECS + tmxsec(localskew), 0);
1100 if (tmxtouch(r->name, TMX_NOTIME, t, TMX_NOTIME, 0))
1101 {
1102 error(ERROR_SYSTEM|1, "%s not sync'd to local time", r->name);
1103 break;
1104 }
1105 if (localtest)
1106 {
1107 tmxsetmtime(&st, t);
1108 break;
1109 }
1110
1111 /*
1112 * some systems try to fix up the local
1113 * remote skew in the utime() call
1114 * >> this never works <<
1115 * members of the club include
1116 * darwin.ppc
1117 * netbsd.i386
1118 */
1119
1120 if (stat(r->name, &st))
1121 {
1122 error(ERROR_SYSTEM|1, "%s not found", r->name);
1123 break;
1124 }
1125 localtest = 1;
1126 q = tmxgetmtime(&st);
1127 if (tmxsec(q) == tmxsec(t))
1128 break;
1129 localskew = tmxsns(tmxsec(q)-tmxsec(t),0);
1130 error(state.regress ? -1 : 1, "the utime(2) or utimes(2) system call is botched for the filesystem containing %s (the current time is adjusted by %lu seconds) -- the state may be out of sync", r->name, tmxsec(localskew));
1131
1132 /*
1133 * the botch may only be for times near "now"
1134 * localskew=1s handles this
1135 */
1136
1137 if (localskew > tmxsns(1,0))
1138 {
1139 t = CURTIME + tmxsns(1,0);
1140 t = tmxsns(tmxsec(t),0);
1141 if (!tmxtouch(r->name, TMX_NOTIME, t, TMX_NOTIME, 0) && !stat(r->name, &st) && tmxgetmtime(&st) == t)
1142 localskew = tmxsns(1,0);
1143 }
1144 }
1145 }
1146 }
1147 }
1148 if (!s)
1149 s = staterule(RULE, r, NiL, 1);
1150 if (sync)
1151 {
1152 s->dynamic |= D_built;
1153 s->attribute = r->attribute;
1154 s->action = r->action;
1155 if (s->prereqs != r->prereqs)
1156 {
1157 if ((r->property & (P_joint|P_target)) != (P_joint|P_target))
1158 freelist(s->prereqs);
1159 s->prereqs = r->prereqs;
1160 }
1161 state.savestate = 1;
1162 }
1163 if (!skip && (s->time != ((r->property & P_virtual) ? r->time : tmxgetmtime(&st)) || zerostate && s->time))
1164 {
1165 s->dynamic &= ~D_lowres;
1166 s->time = zerostate ? 0 : (r->property & P_virtual) ? r->time : tmxgetmtime(&st);
1167 s->event = CURTIME;
1168 state.savestate = 1;
1169 }
1170 return s->time;
1171 }
1172
1173 /*
1174 * return 1 if rule r time matches state s time modulo
1175 * tolerance and low resolution time state
1176 */
1177
1178 int
statetimeq(Rule_t * r,Rule_t * s)1179 statetimeq(Rule_t* r, Rule_t* s)
1180 {
1181 long d;
1182
1183 static int warned;
1184
1185 if (r->time == s->time)
1186 return 1;
1187 if (state.tolerance || (s->dynamic & D_lowres))
1188 {
1189 if (!(d = tmxsec(r->time) - tmxsec(s->time)) && (s->dynamic & D_lowres))
1190 {
1191 s->dynamic &= ~D_lowres;
1192 s->time = r->time;
1193 state.savestate = 1;
1194 return 1;
1195 }
1196 if (d >= -state.tolerance && d <= state.tolerance)
1197 return 1;
1198 }
1199 else if (r->time < s->time && !(r->property & P_state) && tmxsec(r->time) == tmxsec(s->time) && !tmxnsec(r->time))
1200 {
1201 if (!warned)
1202 {
1203 warned = 1;
1204 if (state.warn)
1205 error(1, "file timestamp subsecond truncation");
1206 }
1207 return 1;
1208 }
1209 return 0;
1210 }
1211