1 /*
2  * CDDL HEADER START
3  *
4  * This file and its contents are supplied under the terms of the
5  * Common Development and Distribution License ("CDDL"), version 1.0.
6  * You may use this file only in accordance with the terms of version
7  * 1.0 of the CDDL.
8  *
9  * A full copy of the text of the CDDL should have accompanied this
10  * source.  A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 /*
24  * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
25  * Use is subject to license terms.
26  */
27 /*
28  * @(#)rep.cc 1.25 06/12/12
29  */
30 
31 #pragma	ident	"@(#)rep.cc	1.25	06/12/12"
32 
33 /*
34  * Copyright 2017-2020 J. Schilling
35  *
36  * @(#)rep.cc	1.11 20/11/19 2017-2020 J. Schilling
37  */
38 #include <schily/mconfig.h>
39 #ifndef lint
40 static	UConst char sccsid[] =
41 	"@(#)rep.cc	1.11 20/11/19 2017-2020 J. Schilling";
42 #endif
43 
44 /*
45  *	rep.c
46  *
47  *	This file handles the .nse_depinfo file
48  */
49 
50 /*
51  * Included files
52  */
53 #include <mk/defs.h>
54 #include <mksh/misc.h>		/* retmem() */
55 #include <vroot/report.h>	/* NSE_DEPINFO */
56 
57 #include <schily/stdio.h>
58 #include <schily/wchar.h>
59 #include <schily/schily.h>
60 
61 /*
62  * We cannot use "using std::wcsdup" as wcsdup() is not always
63  * in the std namespace.
64  * The Sun CC compiler in version 4 does not suport using namespace std;
65  * so be careful.
66  */
67 #if !defined(__SUNPRO_CC_COMPAT) || __SUNPRO_CC_COMPAT >= 5
68 using namespace std;		/* needed for wcsdup() */
69 #endif
70 
71 /*
72  * Static variables
73  */
74 static	Recursive_make	recursive_list;
75 static	Recursive_make	*bpatch = &recursive_list;
76 static	Boolean		changed;
77 
78 #ifndef	HAVE_FGETWS
79 wchar_t *
fgetws(wchar_t * ws,int n,FILE * f)80 fgetws(wchar_t *ws, int n, FILE *f)
81 {
82 	char	buf[MAXPATHLEN];
83 	char	*bp = buf;
84 	char	*p;
85 
86 	if (n > sizeof (n))
87 		bp = getmem(n);
88 
89 	p = fgets(bp, n, f);
90 	if (p == NULL) {
91 		ws = (wchar_t *)0;
92 		goto out;
93 	}
94 
95 	if (mbstowcs(ws, buf, n) < 0) {
96 		ws = (wchar_t *)0;
97 		goto out;
98 	}
99 
100 out:
101 	if (bp != buf)
102 		free(bp);
103 	return (ws);
104 }
105 #endif
106 
107 /*
108  * File table of contents
109  */
110 
111 
112 /*
113  *	report_recursive_init()
114  *
115  *	Read the .nse_depinfo file and make a list of all the
116  *	.RECURSIVE entries.
117  *
118  *	Parameters:
119  *
120  *	Static variables used:
121  *		bpatch		Points to slot where next cell should be added
122  *
123  *	Global variables used:
124  *		recursive_name	The Name ".RECURSIVE", compared against
125  */
126 
127 void
report_recursive_init(void)128 report_recursive_init(void)
129 {
130 	char		*search_dir;
131 	char		nse_depinfo[MAXPATHLEN];
132 	FILE		*fp;
133 	int		line_size, line_index;
134 	wchar_t		*line;
135 	wchar_t		*bigger_line;
136 	wchar_t		*colon;
137 	wchar_t		*dollar;
138 	Recursive_make	rp;
139 
140 	/*
141 	 * This routine can be called more than once,  don't do
142 	 * anything after the first time.
143 	 */
144 	if (depinfo_already_read) {
145 		return;
146 	} else {
147 		depinfo_already_read = true;
148 	}
149 
150 	search_dir = getenv(NOCATGETS("NSE_DEP"));
151 	if (search_dir == NULL) {
152 		return;
153 	}
154 	(void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO);
155 	fp = fopen(nse_depinfo, "r");
156 	if (fp == NULL) {
157 		return;
158 	}
159 	line_size = MAXPATHLEN;
160 	line_index = line_size - 1;
161 	line = ALLOC_WC(line_size);
162 	Wstring rns(recursive_name);
163 	wchar_t * wcb = rns.get_string();
164 	while (fgetws(line, line_size, fp) != NULL) {
165 		while (wcslen(line) == line_index) {
166 			if (line[wcslen(line) - 1] == '\n') {
167 				continue;
168 			}
169 			bigger_line = ALLOC_WC(2 * line_size);
170 			wcscpy(bigger_line, line);
171 			retmem(line);
172 			line = bigger_line;
173 			if (fgetws(&line[line_index], line_size, fp) == NULL)
174 				continue;
175 			line_index = 2 * line_index;
176 			line_size = 2 * line_size;
177 		}
178 
179 		colon = (wchar_t *) wcschr(line, (int) colon_char);
180 		if (colon == NULL) {
181 			continue;
182 		}
183 		dollar = (wchar_t *) wcschr(line, (int) dollar_char);
184 		line[wcslen(line) - 1] = (int) nul_char;
185 		if (IS_WEQUALN(&colon[2], wcb,
186 	            (int) recursive_name->hash.length)) {
187 			/*
188 			 * If this entry is an old entry, ignore it
189 			 */
190 			MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
191 			if (dollar == NULL ||
192 			    !IS_WEQUALN(wcs_buffer, (dollar+1) - VER_LEN, VER_LEN)){
193 				continue;
194 			    }
195 			rp = ALLOC(Recursive_make);
196 			(void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
197 			/*
198 			 * set conditional_macro_string if string is present
199 			 */
200 			rp->oldline = (wchar_t *) wcsdup(line);
201 			if ( dollar != NULL ){
202 				rp->cond_macrostring =
203 				    (wchar_t *) wcsdup(dollar - VER_LEN + 1);
204 			}
205 			/*
206 			 * get target name into recursive struct
207 			 */
208 			*colon = (int) nul_char;
209 			rp->target = (wchar_t *) wcsdup(line);
210 			*bpatch = rp;
211 			bpatch = &rp->next;
212 		}
213 	}
214 	(void) fclose(fp);
215 }
216 
217 /*
218  *	report_recursive_dep(target, line)
219  *
220  *	Report a target as recursive.
221  *
222  *	Parameters:
223  *		line		Dependency line reported
224  *
225  *	Static variables used:
226  *		bpatch		Points to slot where next cell should be added
227  *		changed		Written if report set changed
228  */
229 void
report_recursive_dep(Name target,wchar_t * line)230 report_recursive_dep(Name target, wchar_t *line)
231 {
232 	Recursive_make	rp;
233 	wchar_t		rec_buf[STRING_BUFFER_LENGTH];
234 	String_rec	string;
235 
236 	INIT_STRING_FROM_STACK(string, rec_buf);
237 	cond_macros_into_string(target, &string);
238 	/*
239 	 * find an applicable recursive entry, if there isn't one, create it
240 	 */
241 	rp = find_recursive_target(target);
242 	if (rp == NULL) {
243 		rp = ALLOC(Recursive_make);
244 		(void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
245 		wchar_t * wcb = get_wstring(target->string_mb); // XXX Tolik: needs retmem
246                 rp->target = wcb;
247 		rp->newline = (wchar_t *) wcsdup(line);
248 		rp->cond_macrostring = (wchar_t *) wcsdup(rec_buf);
249 		*bpatch = rp;
250 		bpatch = &rp->next;
251 		changed = true;
252 	} else {
253 		if ((rp->oldline != NULL) && !IS_WEQUAL(rp->oldline, line)) {
254 			rp->newline = (wchar_t *) wcsdup(line);
255 			changed = true;
256 		}
257 		rp->removed = false;
258 	}
259 }
260 
261 /*
262  *	find_recursive_target(target)
263  *
264  *	Search the list for a given target.
265  *
266  *	Return value:
267  *				The target cell
268  *
269  *	Parameters:
270  *		target		The target we need
271  *		top_level_target more info used to determinde the
272  *				 target we need
273  *
274  *	Static variables used:
275  *		recursive_list	The list of targets
276  */
277 Recursive_make
find_recursive_target(Name target)278 find_recursive_target(Name target)
279 {
280 	Recursive_make	rp;
281 	String_rec	string;
282 	wchar_t		rec_buf[STRING_BUFFER_LENGTH];
283 
284 	INIT_STRING_FROM_STACK(string, rec_buf);
285 	cond_macros_into_string(target, &string);
286 
287 	Wstring tstr(target);
288 	wchar_t * wcb = tstr.get_string();
289 	for (rp = recursive_list; rp != NULL; rp = rp->next) {
290 		/*
291 		 * If this entry has already been removed, ignore it.
292 		 */
293 		if (rp->removed)
294 			continue;
295 		/*
296 		 * If this target, and the target on the list are the same
297 		 * and if one of them contains conditional macro info, while
298 		 * the other doesn't,  remove this entry from the list of
299 		 * recursive entries.  This can only happen if the Makefile
300 		 * has changed to no longer contain conditional macros.
301 		 */
302 		if (IS_WEQUAL(rp->target, wcb)) {
303 			if (rp->cond_macrostring[VER_LEN] == '\0' &&
304 			    string.buffer.start[VER_LEN] != '\0'){
305 				rp->removed = true;
306 				continue;
307 			} else if (rp->cond_macrostring[VER_LEN] != '\0' &&
308 			    string.buffer.start[VER_LEN] == '\0'){
309 				rp->removed = true;
310 				continue;
311 			}
312 		}
313 		/*
314 		 * If this is not a VERS2 entry,  only need to match
315 		 * the target name.  toptarg information from VERS1 entries
316 		 * are ignored.
317 		 */
318 		MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
319 		if (IS_WEQUALN(wcs_buffer, string.buffer.start, VER_LEN)) {
320 			if (IS_WEQUAL(rp->cond_macrostring,
321 			    string.buffer.start) &&
322 			    IS_WEQUAL(rp->target, wcb)) {
323 				return rp;
324 			}
325 		} else {
326 			if (IS_WEQUAL(rp->target, wcb)) {
327 				return rp;
328 			}
329 		}
330 	}
331 	return NULL;
332 }
333 
334 /*
335  *	remove_recursive_dep(target, top_level_target)
336  *
337  *	Mark a target as no longer recursive.
338  *
339  *	Parameters:
340  *		target		The target we want to remove
341  *		top_level_target target we want to remove must be built from
342  *				 the same top level target
343  *
344  *	Static variables used:
345  *		changed		Written if report set changed
346  */
347 void
remove_recursive_dep(Name target)348 remove_recursive_dep(Name target)
349 {
350 	Recursive_make	rp;
351 
352 	rp = find_recursive_target(target);
353 
354 	if ( rp != NULL ) {
355 		rp->removed = true;
356 		changed = true;
357 		if(rp->target) {
358 			retmem(rp->target);
359 			rp->target = NULL;
360 		}
361 		if(rp->newline) {
362 			retmem(rp->newline);
363 			rp->newline = NULL;
364 		}
365 		if(rp->oldline) {
366 			retmem(rp->oldline);
367 			rp->oldline = NULL;
368 		}
369 		if(rp->cond_macrostring) {
370 			retmem(rp->cond_macrostring);
371 			rp->cond_macrostring = NULL;
372 		}
373 	}
374 }
375 
376 #ifdef NSE
377 /*
378  *	report_recursive_done()
379  *
380  *	Write the .nse_depinfo file.
381  *
382  *	Parameters:
383  *
384  *	Static variables used:
385  *		recursive_list	The list of targets
386  *		changed		Written if report set changed
387  *
388  *	Global variables used:
389  *		recursive_name	The Name ".RECURSIVE", compared against
390  */
391 void
report_recursive_done(void)392 report_recursive_done(void)
393 {
394 	char		*search_dir;
395 	char		nse_depinfo[MAXPATHLEN];
396 	char		tmpfile[MAXPATHLEN];
397 	FILE		*ofp;
398 	FILE		*ifp;
399 	wchar_t		*space;
400 	wchar_t		*data;
401 	wchar_t		*line;
402 	wchar_t		*bigger_line;
403 	int		line_size, line_index;
404 	int		lock_err;
405 	Recursive_make	rp;
406 
407 	if (changed == false) {
408 		return;
409 	}
410 
411 	search_dir = getenv(NOCATGETS("NSE_DEP"));
412 	if (search_dir == NULL) {
413 		return;
414 	}
415 	(void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO);
416 	(void) sprintf(tmpfile, "%s.%d", nse_depinfo, getpid());
417 	ofp = fopen(tmpfile, "w");
418 	if (ofp == NULL) {
419 		(void) fprintf(stderr,
420 			       gettext("Cannot open `%s' for writing\n"),
421 			       tmpfile);
422 		return;
423 	}
424 	(void) sprintf(nse_depinfo_lockfile,
425 		       "%s/%s", search_dir, NSE_DEPINFO_LOCK);
426 	if (lock_err = file_lock(nse_depinfo,
427 				 nse_depinfo_lockfile,
428 				 (int *) &nse_depinfo_locked, 0)) {
429 		(void) fprintf(stderr,
430 			       gettext("writing .RECURSIVE lines to %s\n"),
431 			       tmpfile);
432 		(void) fprintf(stderr,
433 			       gettext("To recover, merge .nse_depinfo.%d with .nse_depinfo\n"),
434 			       getpid(),
435 			       gettext("with .nse_depinfo"));
436 	}
437 
438 	if (nse_depinfo_locked) {
439 		ifp = fopen(nse_depinfo, "r");
440 		if (ifp != NULL) {
441 			/*
442 			 * Copy all the non-.RECURSIVE lines from
443 			 * the old file to the new one.
444 			 */
445 			line_size = MAXPATHLEN;
446 			line_index = line_size - 1;
447 			line = ALLOC_WC(line_size);
448 			while (fgetws(line, line_size, ifp) != NULL) {
449 				while (wcslen(line) == line_index) {
450 					if (line[wcslen(line) - 1] == '\n') {
451 						continue;
452 					}
453 					bigger_line = ALLOC_WC(2 * line_size);
454 					wcscpy(bigger_line, line);
455 					retmem(line);
456 					line = bigger_line;
457 					if (fgetws(&line[line_index],
458 						  line_size, ifp) == NULL)
459 					  continue;
460 					line_index = 2 * line_index;
461 					line_size = 2 * line_size;
462 				}
463 
464 				space = wcschr(line, (int) space_char);
465 				if (space != NULL &&
466 				    IS_WEQUALN(&space[1],
467 					      recursive_name->string,
468 					      (int) recursive_name->hash.length)) {
469 					continue;
470 				}
471 				WCSTOMBS(mbs_buffer, line);
472 				(void) fprintf(ofp, "%s", mbs_buffer);
473 			}
474 			(void) fclose(ifp);
475 		}
476 	}
477 
478 	/*
479 	 * Write out the .RECURSIVE lines.
480 	 */
481 	for (rp = recursive_list; rp != NULL; rp = rp->next) {
482 		if (rp->removed) {
483 			continue;
484 		}
485 		if (rp->newline != NULL) {
486 			data = rp->newline;
487 		} else {
488 			data = rp->oldline;
489 		}
490 		if (data != NULL) {
491 			WCSTOMBS(mbs_buffer, data);
492 			(void) fprintf(ofp, "%s\n", mbs_buffer);
493 		}
494 	}
495 	(void) fclose(ofp);
496 
497 	if (nse_depinfo_locked) {
498 		(void) rename(tmpfile, nse_depinfo);
499 		(void) unlink(nse_depinfo_lockfile);
500 		nse_depinfo_locked = false;
501 		nse_depinfo_lockfile[0] = '\0';
502 		(void) chmod(nse_depinfo, 0666);
503 	}
504 }
505 #endif // NSE
506 
507 /* gather_recursive_deps()
508  *
509  *	Create or update list of recursive targets.
510  */
511 void
gather_recursive_deps(void)512 gather_recursive_deps(void)
513 {
514 	Name_set::iterator	np, e;
515 	String_rec		rec;
516 	wchar_t			rec_buf[STRING_BUFFER_LENGTH];
517 	register Property	lines;
518 	Boolean			has_recursive;
519 	Dependency		dp;
520 
521 	report_recursive_init();
522 
523 	/* Go thru all targets and dump recursive dependencies */
524 	for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
525 		if (np->has_recursive_dependency){
526 			has_recursive = false;
527 			/*
528 			 * start .RECURSIVE line with target:
529 			 */
530 			INIT_STRING_FROM_STACK(rec, rec_buf);
531 			APPEND_NAME(np, &rec, FIND_LENGTH);
532 			append_char((int) colon_char, &rec);
533 			append_char((int) space_char, &rec);
534 
535 			for (lines = get_prop(np->prop,recursive_prop);
536 			    lines != NULL;
537 			    lines = get_prop(lines->next, recursive_prop)) {
538 				/*
539 				 * if entry is already in depinfo
540 				 * file or entry was not built, ignore it
541 				 */
542 				if (lines->body.recursive.in_depinfo)
543 					continue;
544 				if (!lines->body.recursive.has_built)
545 					continue;
546 				has_recursive = true;
547 				lines->body.recursive.in_depinfo=true;
548 
549 				/*
550 				* Write the remainder of the
551 				* .RECURSIVE line
552 				*/
553 				APPEND_NAME(recursive_name, &rec,
554 				    FIND_LENGTH);
555 				append_char((int) space_char, &rec);
556 				APPEND_NAME(lines->body.recursive.directory,
557 					&rec, FIND_LENGTH);
558 				append_char((int) space_char, &rec);
559 				APPEND_NAME(lines->body.recursive.target,
560 					&rec, FIND_LENGTH);
561 				append_char((int) space_char, &rec);
562 
563 				/* Complete list of makefiles used */
564 				for (dp = lines->body.recursive.makefiles;
565 				    dp != NULL;
566 				    dp = dp->next) {
567 					APPEND_NAME(dp->name, &rec,  FIND_LENGTH);
568 					append_char((int) space_char, &rec);
569 				}
570 			}
571 			/*
572 			 * dump list of conditional targets,
573 			 * and report recursive entry, if needed
574 			 */
575 			cond_macros_into_string(np, &rec);
576 			if (has_recursive){
577 				report_recursive_dep(np, rec.buffer.start);
578 			}
579 
580 		} else if ( np->has_built ) {
581 			remove_recursive_dep(np);
582 		}
583 	}
584 }
585 
586