Line data Source code
1 : /* RCS $Id: sysintf.c,v 1.13 2008-03-05 18:30:58 kz Exp $
2 : --
3 : -- SYNOPSIS
4 : -- System independent interface
5 : --
6 : -- DESCRIPTION
7 : -- These are the routines constituting the system interface.
8 : -- The system is taken to be essentially POSIX conformant.
9 : -- The original code was extensively revised by T J Thompson at MKS,
10 : -- and the library cacheing was added by Eric Gisin at MKS. I then
11 : -- revised the code yet again, to improve the lib cacheing, and to
12 : -- make it more portable.
13 : --
14 : -- The following is a list of routines that are required by this file
15 : -- in order to work. These routines are provided as functions by the
16 : -- standard C lib of the target system or as #defines in system/sysintf.h
17 : -- or via appropriate C code in the system/ directory for the given
18 : -- system.
19 : --
20 : -- The first group must be provided by a file in the system/ directory
21 : -- the second group is ideally provided by the C lib. However, there
22 : -- are instances where the C lib implementation of the specified routine
23 : -- does not exist, or is incorrect. In these instances the routine
24 : -- must be provided by the the user in the system/ directory of dmake.
25 : -- (For example, the bsd/ dir contains code for putenv(), and tempnam())
26 : --
27 : -- DMAKE SPECIFIC:
28 : -- seek_arch()
29 : -- touch_arch()
30 : -- void_lcache()
31 : -- runargv()
32 : -- DMSTAT()
33 : -- Remove_prq()
34 : --
35 : -- C-LIB SPECIFIC: (should be present in your C-lib)
36 : -- utime()
37 : -- time()
38 : -- getenv()
39 : -- putenv()
40 : -- getcwd()
41 : -- signal()
42 : -- chdir()
43 : -- tempnam()
44 : --
45 : -- AUTHOR
46 : -- Dennis Vadura, dvadura@dmake.wticorp.com
47 : --
48 : -- WWW
49 : -- http://dmake.wticorp.com/
50 : --
51 : -- COPYRIGHT
52 : -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
53 : --
54 : -- This program is NOT free software; you can redistribute it and/or
55 : -- modify it under the terms of the Software License Agreement Provided
56 : -- in the file <distribution-root>/readme/license.txt.
57 : --
58 : -- LOG
59 : -- Use cvs log to obtain detailed change logs.
60 : */
61 :
62 : #include "extern.h"
63 :
64 : /* The following definition controls the use of GetModuleFileName() */
65 : #if defined(_MSC_VER) || defined(__MINGW32__)
66 : # define HAVE_GETMODULEFILENAMEFUNC 1
67 :
68 : /* this is needed for the _ftime call below. Only needed here. */
69 : # include <sys/timeb.h>
70 : #endif
71 :
72 : /* for cygwin_conv_to_posix_path() in Prolog() and for cygdospath()*/
73 : #if __CYGWIN__
74 : # include <sys/cygwin.h>
75 : #endif
76 :
77 : #include "sysintf.h"
78 : #if HAVE_ERRNO_H
79 : # include <errno.h>
80 : #else
81 : extern int errno;
82 : #endif
83 :
84 : /*
85 : ** Tries to stat the file name. Returns 0 if the file
86 : ** does not exist. Note that if lib is not null it tries to stat
87 : ** the name found inside lib.
88 : **
89 : ** If member is NOT nil then look for the library object which defines the
90 : ** symbol given by name. If found DmStrDup the name and return make the
91 : ** pointer pointed at by sym point at it. Not handled for now!
92 : */
93 : static time_t
94 0 : really_dostat(name, buf)
95 : char *name;
96 : struct stat *buf;
97 : {
98 0 : return( ( DMSTAT(name,buf)==-1
99 0 : || (STOBOOL(Augmake) && (buf->st_mode & S_IFDIR)))
100 : ? (time_t)0L
101 0 : : (time_t) buf->st_mtime
102 : );
103 : }
104 :
105 :
106 : PUBLIC time_t
107 37761 : Do_stat(name, lib, member, force)
108 : char *name;
109 : char *lib;
110 : char **member;
111 : int force;
112 : {
113 : struct stat buf;
114 : time_t seek_arch();
115 :
116 37761 : if( member != NIL(char *) )
117 0 : Fatal("Library symbol names not supported");
118 :
119 37761 : buf.st_mtime = (time_t)0L;
120 37761 : if( lib != NIL(char) )
121 0 : return( seek_arch(Basename(name), lib) );
122 37761 : else if( strlen(Basename(name)) > NameMax ) {
123 0 : Warning( "Filename [%s] longer than value of NAMEMAX [%d].\n\
124 : Assume unix time 0.\n", Basename(name), NameMax );
125 0 : return((time_t)0L);
126 : }
127 37761 : else if( STOBOOL(UseDirCache) )
128 37761 : return(CacheStat(name,force));
129 : else
130 0 : return(really_dostat(name,&buf));
131 : }
132 :
133 :
134 : /* Touch existing file to force modify time to present.
135 : */
136 : PUBLIC int
137 0 : Do_touch(name, lib, member)
138 : char *name;
139 : char *lib;
140 : char **member;
141 : {
142 0 : if( member != NIL(char *) )
143 0 : Fatal("Library symbol names not supported");
144 :
145 0 : if (lib != NIL(char))
146 0 : return( touch_arch(Basename(name), lib) );
147 0 : else if( strlen(Basename(name)) > NameMax ) {
148 0 : Warning( "Filename [%s] longer than value of NAMEMAX [%d].\n\
149 : File timestamp not updated to present time.\n", Basename(name), NameMax );
150 0 : return(-1);
151 : }
152 : else
153 : #ifdef HAVE_UTIME_NULL
154 0 : return( utime(name, NULL) );
155 : #else
156 : # error "Utime NULL not supported"
157 : #endif
158 : }
159 :
160 :
161 :
162 : PUBLIC void
163 0 : Void_lib_cache( lib_name, member_name )/*
164 : =========================================
165 : Void the library cache for lib lib_name, and member member_name. */
166 : char *lib_name;
167 : char *member_name;
168 : {
169 0 : VOID_LCACHE( lib_name, member_name );
170 0 : }
171 :
172 :
173 :
174 : /*
175 : ** return the current time
176 : */
177 : PUBLIC time_t
178 1672 : Do_time()
179 : {
180 1672 : return (time( NIL(time_t) ));
181 : }
182 :
183 :
184 :
185 : /*
186 : ** Print profiling information
187 : */
188 : PUBLIC void
189 0 : Do_profile_output( text, mtype, target )
190 : char *text;
191 : uint16 mtype;
192 : CELLPTR target;
193 : {
194 :
195 : time_t time_sec;
196 : uint32 time_msec;
197 : char *tstrg;
198 : char *tname;
199 :
200 : #ifdef HAVE_GETTIMEOFDAY
201 : struct timeval timebuffer;
202 0 : gettimeofday(&timebuffer, NULL);
203 0 : time_sec = timebuffer.tv_sec;
204 0 : time_msec = timebuffer.tv_usec/1000;
205 : #else
206 : #if defined(_MSC_VER) || defined(__MINGW32__)
207 : struct _timeb timebuffer;
208 : _ftime( &timebuffer );
209 : time_sec = timebuffer.time;
210 : time_msec = timebuffer.millitm;
211 : # else
212 : time_sec = time( NIL(time_t) );
213 : time_msec = 0;
214 : # endif
215 : #endif
216 :
217 0 : tname = target->CE_NAME;
218 0 : if( mtype & M_TARGET ) {
219 0 : tstrg = "target";
220 : /* Don't print special targets .TARGETS and .ROOT */
221 0 : if( tname[0] == '.' && (strcmp(".TARGETS", tname) == 0 || \
222 0 : strcmp(".ROOT", tname) == 0) ) {
223 : return;
224 : }
225 : } else {
226 0 : tstrg = "recipe";
227 : }
228 :
229 : /* Don't print shell escape targets if not especially requested. */
230 0 : if( (target->ce_attr & A_SHELLESC) && !(Measure & M_SHELLESC) ) {
231 : return;
232 : }
233 :
234 : /* Print absolute path if requested. */
235 0 : if( !(target->ce_attr & A_SHELLESC) && (Measure & M_ABSPATH) ) {
236 0 : printf("%s %s %lu.%.3u %s%s%s\n",text, tstrg, time_sec, time_msec, Pwd, DirSepStr, tname);
237 : } else {
238 0 : printf("%s %s %lu.%.3u %s\n",text, tstrg, time_sec, time_msec, tname);
239 : }
240 : }
241 :
242 :
243 :
244 : PUBLIC int
245 2184 : Do_cmnd(cmd, group, do_it, target, cmnd_attr, last)/*
246 : =====================================================
247 : Execute the string passed in as a command and return
248 : the return code. The command line arguments are
249 : assumed to be separated by spaces or tabs. The first
250 : such argument is assumed to be the command.
251 :
252 : If group is true then this is a group of commands to be fed to the
253 : the shell as a single unit. In this case cmd is of the form
254 : "file" indicating the file that should be read by the shell
255 : in order to execute the command group.
256 :
257 : If Wait_for_completion is TRUE add the A_WFC attribute to the new
258 : process.
259 : */
260 : char **cmd; /* Simulate a reference to *cmd. */
261 : int group; /* if set cmd contains the filename of a (group-)shell
262 : * script. */
263 : int do_it; /* Only execute cmd if not set to null. */
264 : CELLPTR target;
265 : t_attr cmnd_attr; /* Attributes for current cmnd. */
266 : int last; /* Last recipe line in target. */
267 : {
268 : int i;
269 :
270 : DB_ENTER( "Do_cmnd" );
271 :
272 2184 : if( !do_it ) {
273 0 : if( last && !Doing_bang ) {
274 : /* Don't execute, just update the target when using '-t'
275 : * switch. */
276 0 : Update_time_stamp( target );
277 : }
278 0 : DB_RETURN( 0 );
279 : }
280 :
281 : /* Stop making the rest of the recipies for this target if an error occurred
282 : * but the Continue (-k) flag is set to build as much as possible. */
283 2184 : if ( target->ce_attr & A_ERROR ) {
284 0 : if ( last ) {
285 0 : Update_time_stamp( target );
286 : }
287 0 : DB_RETURN( 0 );
288 : }
289 :
290 2184 : if( Max_proc == 1 ) Wait_for_completion = TRUE;
291 :
292 : /* Tell runargv() to wait if needed. */
293 2184 : if( Wait_for_completion ) cmnd_attr |= A_WFC;
294 :
295 : /* remove leading whitespace - This should never trigger! */
296 2184 : if( iswhite(**cmd) ) {
297 : char *p;
298 0 : if( (p = DmStrSpn(*cmd," \t") ) != *cmd )
299 0 : strcpy(*cmd,p);
300 : }
301 :
302 : /* set shell if shell metas are found */
303 2184 : if( (cmnd_attr & A_SHELL) || group || (*DmStrPbrk(*cmd, Shell_metas)!='\0') )
304 402 : cmnd_attr |= A_SHELL; /* If group is TRUE this doesn't hurt. */
305 :
306 : /* runargv() returns either 0 or 1, 0 ==> command executed, and
307 : * we waited for it to return, 1 ==> command started and is still
308 : * running. */
309 2184 : i = runargv(target, group, last, cmnd_attr, cmd);
310 :
311 2184 : DB_RETURN( i );
312 : }
313 :
314 :
315 : #define MINARGV 64
316 :
317 : PUBLIC char **
318 412 : Pack_argv( group, shell, cmd )/*
319 : ================================
320 : Take a command and pack it into an argument vector to be executed.
321 : If group is true cmd holds the group script file.
322 : */
323 : int group;
324 : int shell;
325 : char **cmd; /* Simulate a reference to *cmd. */
326 : {
327 : static char **av = NIL(char *);
328 : static int avs = 0;
329 412 : int i = 0;
330 : char *s; /* Temporary string pointer. */
331 :
332 412 : if( av == NIL(char *) ) {
333 184 : TALLOC(av, MINARGV, char*);
334 184 : avs = MINARGV;
335 : }
336 412 : av[0] = NIL(char);
337 :
338 412 : if (**cmd) {
339 814 : if( shell||group ) {
340 402 : char* sh = group ? GShell : Shell;
341 :
342 402 : if( sh != NIL(char) ) {
343 402 : av[i++] = sh;
344 402 : if( (av[i] = (group?GShell_flags:Shell_flags)) != NIL(char) ) i++;
345 :
346 402 : if( shell && Shell_quote && *Shell_quote ) {
347 : /* Enclose the shell command with SHELLCMDQUOTE. */
348 0 : s = DmStrJoin(Shell_quote, *cmd, -1, FALSE);
349 0 : FREE(*cmd);
350 0 : *cmd = DmStrJoin(s, Shell_quote, -1, TRUE);
351 : }
352 402 : av[i++] = *cmd;
353 :
354 : #if defined(USE_CREATEPROCESS)
355 : /* CreateProcess() needs one long command line. */
356 : av[0] = DmStrAdd(av[0], av[1], FALSE);
357 : av[1] = NIL(char);
358 : /* i == 3 means Shell_flags are given. */
359 : if( i == 3 ) {
360 : s = av[0];
361 : av[0] = DmStrAdd(s, av[2], FALSE);
362 : FREE(s);
363 : av[2] = NIL(char);
364 : }
365 : /* The final free of cmd will free the concated command line. */
366 : FREE(*cmd);
367 : *cmd = av[0];
368 : #endif
369 402 : av[i] = NIL(char);
370 : }
371 : else
372 0 : Fatal("%sSHELL macro not defined", group?"GROUP":"");
373 : }
374 : else {
375 10 : char *tcmd = *cmd;
376 :
377 : #if defined(USE_CREATEPROCESS)
378 : /* CreateProcess() needs one long command line, fill *cmd
379 : * into av[0]. */
380 : while( iswhite(*tcmd) ) ++tcmd;
381 : if( *tcmd ) av[i++] = tcmd;
382 : #else
383 : /* All other exec/spawn functions need the parameters separated
384 : * in the argument vector. */
385 : do {
386 : /* Fill *cmd into av[]. Whitespace is converted into '\0' to
387 : * terminate each av[] member. */
388 32 : while( iswhite(*tcmd) ) ++tcmd;
389 32 : if( *tcmd ) av[i++] = tcmd;
390 :
391 32 : while( *tcmd != '\0' && !iswhite(*tcmd) ) ++tcmd;
392 32 : if( *tcmd ) *tcmd++ = '\0';
393 :
394 : /* dynamically increase av size. */
395 32 : if( i == avs ) {
396 0 : avs += MINARGV;
397 0 : av = (char **) realloc( av, avs*sizeof(char *) );
398 : }
399 32 : } while( *tcmd );
400 : #endif
401 :
402 10 : av[i] = NIL(char);
403 : }
404 : }
405 :
406 412 : return(av);
407 : }
408 :
409 :
410 : /*
411 : ** Return the value of ename from the environment
412 : ** if ename is not defined in the environment then
413 : ** NIL(char) should be returned
414 : */
415 : PUBLIC char *
416 1298 : Read_env_string(ename)
417 : char *ename;
418 : {
419 1298 : return( getenv(ename) );
420 : }
421 :
422 :
423 : /*
424 : ** Set the value of the environment string ename to value.
425 : ** Returns 0 if success, non-zero if failure
426 : */
427 : PUBLIC int
428 2600 : Write_env_string(ename, value)
429 : char *ename;
430 : char *value;
431 : {
432 : #if defined(HAVE_SETENV)
433 :
434 2600 : return( setenv(ename, value, 1) );
435 :
436 : #else /* !HAVE_SETENV */
437 :
438 : char* p;
439 : char* envstr = DmStrAdd(ename, value, FALSE);
440 :
441 : p = envstr+strlen(ename); /* Don't change this code, DmStrAdd does not */
442 : *p++ = '='; /* add the space if *value is 0, it does */
443 : if( !*value ) *p = '\0'; /* allocate enough memory for one though. */
444 :
445 : return( putenv(envstr) ); /* Possibly leaking 'envstr' */
446 :
447 : #endif /* !HAVE_SETENV */
448 : }
449 :
450 :
451 : PUBLIC void
452 184 : ReadEnvironment()
453 : {
454 : extern char **Rule_tab;
455 : #if !defined(_MSC_VER)
456 : #if defined(__BORLANDC__) && __BORLANDC__ >= 0x500
457 : extern char ** _RTLENTRY _EXPDATA environ;
458 : #else
459 : extern char **environ;
460 : #endif
461 : #endif
462 : char **rsave;
463 :
464 : #if !defined(__ZTC__) && !defined(_MPW)
465 : # define make_env()
466 : # define free_env()
467 : #else
468 : void make_env();
469 : void free_env();
470 : #endif
471 :
472 : make_env();
473 :
474 184 : rsave = Rule_tab;
475 184 : Rule_tab = environ;
476 184 : Readenv = TRUE;
477 :
478 184 : Parse( NIL(FILE) );
479 :
480 184 : Readenv = FALSE;
481 184 : Rule_tab = rsave;
482 :
483 : free_env();
484 184 : }
485 :
486 :
487 :
488 : /*
489 : ** All we have to catch is SIGINT
490 : */
491 : PUBLIC void
492 188 : Catch_signals(fn)
493 : void (*fn)(int);
494 : {
495 : /* FIXME: Check this and add error handling. */
496 188 : if( (void (*)(int)) signal(SIGINT, SIG_IGN) != (void (*)(int))SIG_IGN )
497 188 : signal( SIGINT, fn );
498 188 : if( (void (*)(int)) signal(SIGQUIT, SIG_IGN) != (void (*)(int))SIG_IGN )
499 188 : signal( SIGQUIT, fn );
500 188 : }
501 :
502 :
503 :
504 : /*
505 : ** Clear any previously set signals
506 : */
507 : PUBLIC void
508 184 : Clear_signals()
509 : {
510 184 : if( (void (*)())signal(SIGINT, SIG_IGN) != (void (*)())SIG_IGN )
511 184 : signal( SIGINT, SIG_DFL );
512 184 : if( (void (*)())signal(SIGQUIT, SIG_IGN) != (void (*)())SIG_IGN )
513 184 : signal( SIGQUIT, SIG_DFL );
514 184 : }
515 :
516 :
517 :
518 : /*
519 : ** Set program name
520 : */
521 : PUBLIC void
522 188 : Prolog(argc, argv)
523 : int argc;
524 : char* argv[];
525 : {
526 188 : Pname = (argc == 0) ? DEF_MAKE_PNAME : argv[0];
527 :
528 : /* Only some native Windows compilers provide this functionality. */
529 : #ifdef HAVE_GETMODULEFILENAMEFUNC
530 : if( (AbsPname = MALLOC( PATH_MAX, char)) == NIL(char) ) No_ram();
531 : GetModuleFileName(NULL, AbsPname, PATH_MAX*sizeof(char));
532 : #else
533 188 : AbsPname = "";
534 : #endif
535 :
536 : #if __CYGWIN__
537 : /* Get the drive letter prefix used by cygwin. */
538 : if ( (CygDrvPre = MALLOC( PATH_MAX, char)) == NIL(char) )
539 : No_ram();
540 : else {
541 : int err = cygwin_conv_to_posix_path("c:", CygDrvPre);
542 : if (err)
543 : Fatal( "error converting \"%s\" - %s\n",
544 : CygDrvPre, strerror (errno));
545 : if( (CygDrvPreLen = strlen(CygDrvPre)) == 2 ) {
546 : /* No prefix */
547 : *CygDrvPre = '\0';
548 : CygDrvPreLen = 0;
549 : } else {
550 : /* Cut away the directory letter. */
551 : CygDrvPre[CygDrvPreLen-2] = '\0';
552 : /* Cut away the leading '/'. We don't free the pointer, i.e. choose
553 : * the easy way. */
554 : CygDrvPre++;
555 : CygDrvPreLen -= 3;
556 : }
557 : }
558 : #endif
559 :
560 : /* DirSepStr is used from Clean_path() in Def_cell(). Set it preliminary
561 : * here, it will be redefined later in Create_macro_vars() in imacs.c. */
562 188 : DirSepStr = "/";
563 :
564 188 : Root = Def_cell( ".ROOT" );
565 188 : Targets = Def_cell( ".TARGETS" );
566 188 : Add_prerequisite(Root, Targets, FALSE, FALSE);
567 :
568 188 : Targets->ce_flag = Root->ce_flag = F_RULES|F_TARGET|F_STAT;
569 188 : Targets->ce_attr = Root->ce_attr = A_NOSTATE|A_PHONY;
570 :
571 188 : Root->ce_flag |= F_MAGIC;
572 188 : Root->ce_attr |= A_SEQ;
573 :
574 188 : tzset();
575 188 : }
576 :
577 :
578 :
579 : /*
580 : ** Do any clean up for exit.
581 : */
582 : PUBLIC void
583 188 : Epilog(ret_code)
584 : int ret_code;
585 : {
586 188 : Write_state();
587 188 : Unlink_temp_files(Root);
588 : Hook_std_writes(NIL(char)); /* For MSDOS tee (-F option) */
589 188 : exit( ret_code );
590 : }
591 :
592 :
593 :
594 : /*
595 : ** Use the built-in functions of the operating system to get the current
596 : ** working directory.
597 : */
598 : PUBLIC char *
599 4556 : Get_current_dir()
600 : {
601 : static char buf[PATH_MAX+2];
602 :
603 4556 : if( !getcwd(buf, sizeof(buf)) )
604 0 : Fatal("Internal Error: Error when calling getcwd()!");
605 :
606 : #ifdef __EMX__
607 : char *slash;
608 : slash = buf;
609 : while( (slash=strchr(slash,'/')) )
610 : *slash = '\\';
611 : #endif
612 :
613 4556 : return buf;
614 : }
615 :
616 :
617 :
618 : /*
619 : ** change working directory
620 : */
621 : PUBLIC int
622 7228 : Set_dir(path)
623 : char* path;
624 : {
625 7228 : return( chdir(path) );
626 : }
627 :
628 :
629 :
630 : /*
631 : ** return switch char
632 : */
633 : PUBLIC char
634 188 : Get_switch_char()
635 : {
636 188 : return( getswitchar() );
637 : }
638 :
639 :
640 746 : int Create_temp(tmpdir, path)/*
641 : ===============================
642 : Create a temporary file and open with exclusive access
643 : Path is updated with the filename and the file descriptor
644 : is returned. Note that the new name should be freed when
645 : the file is removed.
646 : */
647 : char *tmpdir;
648 : char **path;
649 : {
650 : int fd; /* file descriptor */
651 :
652 : #if defined(HAVE_MKSTEMP)
653 : mode_t mask;
654 :
655 746 : *path = DmStrJoin( tmpdir, DirSepStr, -1, FALSE);
656 746 : *path = DmStrJoin( *path, "mkXXXXXX", -1, TRUE );
657 :
658 746 : mask = umask(0066);
659 746 : fd = mkstemp( *path );
660 746 : umask(mask);
661 :
662 : #elif defined(HAVE_TEMPNAM)
663 : char pidbuff[32];
664 : #if _MSC_VER >= 1300
665 : /* Create more unique filename for .NET2003 and newer. */
666 : long npid;
667 : long nticks;
668 :
669 : npid = _getpid();
670 : nticks = GetTickCount() & 0xfff;
671 : sprintf(pidbuff,"mk%d_%d_",npid,nticks);
672 : #else
673 : sprintf(pidbuff,"mk");
674 : #endif
675 : *path = tempnam(tmpdir, pidbuff);
676 : fd = open(*path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0600);
677 : #else
678 :
679 : #error mkstemp() or tempnam() is needed
680 :
681 : #endif
682 :
683 746 : return fd;
684 : }
685 :
686 :
687 : PUBLIC FILE*
688 746 : Get_temp(path, mode)/*
689 : ======================
690 : Generate a temporary file name and open the file for writing.
691 : If a name cannot be generated or the file cannot be opened
692 : return -1, else return the fileno of the open file.
693 : and update the source file pointer to point at the new file name.
694 : Note that the new name should be freed when the file is removed.
695 : The file stream is opened with the given mode.
696 : */
697 : char **path;
698 : char *mode;
699 : {
700 : int fd;
701 : FILE *fp;
702 : char *tmpdir;
703 746 : int tries = 20;
704 :
705 746 : tmpdir = Read_env_string( "TMPDIR" );
706 746 : if( tmpdir == NIL(char) )
707 0 : tmpdir = "/tmp";
708 :
709 1492 : while( --tries )
710 : {
711 : /* This sets path to the name of the created temp file. */
712 746 : if( (fd = Create_temp(tmpdir, path)) != -1)
713 746 : break;
714 :
715 0 : free(*path); /* free var if creating temp failed. */
716 : }
717 :
718 746 : if( fd != -1)
719 : {
720 746 : Def_macro( "TMPFILE", DO_WINPATH(*path), M_MULTI|M_EXPANDED );
721 : /* associate stream with file descriptor */
722 746 : fp = fdopen(fd, mode);
723 : }
724 : else
725 0 : fp = NIL(FILE);
726 :
727 746 : return fp;
728 : }
729 :
730 :
731 : PUBLIC FILE *
732 362 : Start_temp( suffix, cp, fname )/*
733 : =================================
734 : Open a new temporary file and set it up for writing. The file is linked
735 : to cp and will be removed if once the target is finished.
736 : If a suffix for the temporary files is requested two temporary files are
737 : created. This is done because the routines that create a save temporary
738 : file do not provide a definable suffix. The first (provided by Get_temp())
739 : is save and unique and the second file is generated by adding the desired
740 : suffix the the first temporary file. The extra file is also linked to cp
741 : so that it gets removed later.
742 : The function returns the FILE pointer to the temporary file (with suffix
743 : if specified) and leaves the file name in *fname.
744 : */
745 : char *suffix;
746 : CELLPTR cp;
747 : char **fname;
748 : {
749 : FILE *fp, *fp2;
750 : char *tmpname;
751 : char *name;
752 : char *fname_suff;
753 :
754 362 : name = (cp != NIL(CELL))?cp->CE_NAME:"makefile text";
755 :
756 : /* This sets tmpname to the name that was used. */
757 362 : if( (fp = Get_temp(&tmpname, "w")) == NIL(FILE) )
758 0 : Open_temp_error( tmpname, name );
759 :
760 : /* Don't free tmpname, it's stored in a FILELIST member in Link_temp(). */
761 362 : Link_temp( cp, fp, tmpname );
762 362 : *fname = tmpname;
763 :
764 : /* As Get_temp() doesn't provide a definable suffix (anymore) we create an
765 : * additional temporary file with that suffix. */
766 362 : if ( suffix && *suffix ) {
767 :
768 : #ifdef HAVE_MKSTEMP
769 : /* Only use umask if we are also using mkstemp - this basically
770 : * avoids using the incompatible implementation from MSVC. */
771 : mode_t mask;
772 :
773 0 : mask = umask(0066);
774 : #endif
775 :
776 0 : fname_suff = DmStrJoin( tmpname, suffix, -1, FALSE );
777 :
778 : /* Overwrite macro, Get_temp didn't know of the suffix. */
779 0 : Def_macro( "TMPFILE", DO_WINPATH(fname_suff), M_MULTI|M_EXPANDED );
780 :
781 0 : if( (fp2 = fopen(fname_suff, "w" )) == NIL(FILE) )
782 0 : Open_temp_error( fname_suff, name );
783 : #ifdef HAVE_MKSTEMP
784 0 : umask(mask);
785 : #endif
786 :
787 : /* Don't free fname_suff. */
788 0 : Link_temp( cp, fp2, fname_suff );
789 0 : fp = fp2;
790 0 : *fname = fname_suff;
791 : }
792 :
793 362 : return( fp );
794 : }
795 :
796 :
797 : /*
798 : ** Issue an error on failing to open a temporary file
799 : */
800 : PUBLIC void
801 0 : Open_temp_error( tmpname, name )
802 : char *tmpname;
803 : char *name;
804 : {
805 0 : Fatal("Cannot open temp file `%s' while processing `%s'", tmpname, name );
806 0 : }
807 :
808 :
809 : /*
810 : ** Link a temp file onto the list of files.
811 : */
812 : PUBLIC void
813 362 : Link_temp( cp, fp, fname )
814 : CELLPTR cp;
815 : FILE *fp;
816 : char *fname;
817 : {
818 : FILELISTPTR new;
819 :
820 362 : if( cp == NIL(CELL) ) cp = Root;
821 :
822 362 : TALLOC( new, 1, FILELIST );
823 :
824 362 : new->fl_next = cp->ce_files;
825 362 : new->fl_name = fname;
826 362 : new->fl_file = fp; /* indicates temp file is open */
827 :
828 362 : cp->ce_files = new;
829 362 : }
830 :
831 :
832 : /*
833 : ** Close a previously used temporary file.
834 : */
835 : PUBLIC void
836 362 : Close_temp(cp, file)
837 : CELLPTR cp;
838 : FILE *file;
839 : {
840 : FILELISTPTR fl;
841 362 : if( cp == NIL(CELL) ) cp = Root;
842 :
843 362 : for( fl=cp->ce_files; fl && fl->fl_file != file; fl=fl->fl_next );
844 362 : if( fl ) {
845 362 : fl->fl_file = NIL(FILE);
846 362 : fclose(file);
847 : }
848 362 : }
849 :
850 :
851 : /*
852 : ** Clean-up, and close all temporary files associated with a target.
853 : */
854 : PUBLIC void
855 2430 : Unlink_temp_files( cp )/*
856 : ==========================
857 : Unlink the tempfiles if any exist. Make sure you close the files first
858 : though. This ensures that under DOS there is no disk space lost. */
859 : CELLPTR cp;
860 : {
861 : FILELISTPTR cur, next;
862 :
863 4860 : if( cp == NIL(CELL) || cp->ce_files == NIL(FILELIST) ) return;
864 :
865 534 : for( cur=cp->ce_files; cur != NIL(FILELIST); cur=next ) {
866 362 : next = cur->fl_next;
867 :
868 362 : if( cur->fl_file ) fclose( cur->fl_file );
869 :
870 362 : if( Verbose & V_LEAVE_TMP )
871 0 : fprintf( stderr, "%s: Left temp file [%s]\n", Pname, cur->fl_name );
872 : else
873 362 : (void) Remove_file( cur->fl_name );
874 :
875 362 : FREE(cur->fl_name);
876 362 : FREE(cur);
877 : }
878 :
879 172 : cp->ce_files = NIL(FILELIST);
880 : }
881 :
882 :
883 : PUBLIC void
884 2184 : Handle_result(status, ignore, abort_flg, target)/*
885 : ==================================================
886 : Handle return value of recipe.
887 : */
888 : int status;
889 : int ignore;
890 : int abort_flg;
891 : CELLPTR target;
892 : {
893 4368 : status = ((status&0xff)==0 ? status>>8 /* return from exit() */
894 2184 : : (status & 0xff)==SIGTERM ? -1 /* terminated from SIGTERM */
895 0 : : (status & 0x7f)+128); /* terminated from signal
896 : * ( =status-128 ) */
897 :
898 2184 : if( status ) {
899 0 : if( !abort_flg ) {
900 : char buf[512];
901 :
902 0 : sprintf(buf, "%s: Error code %d, while making '%s'",
903 : Pname, status, target->ce_fname );
904 :
905 0 : if( ignore || Continue ) {
906 0 : if (!(Glob_attr & A_SILENT)) {
907 0 : strcat(buf, " (Ignored" );
908 :
909 0 : if ( Continue ) {
910 : /* Continue after error if '-k' was used. */
911 0 : strcat(buf,",Continuing");
912 0 : target->ce_attr |= A_ERROR;
913 : }
914 0 : strcat(buf,")");
915 0 : if (Verbose)
916 0 : fprintf(stderr, "%s\n", buf);
917 : }
918 :
919 0 : if( target->ce_attr & A_ERRREMOVE
920 0 : && Remove_file( target->ce_fname ) == 0
921 0 : && !(Glob_attr & A_SILENT))
922 0 : fprintf(stderr,"%s: '%s' removed.\n", Pname, target->ce_fname);
923 : }
924 : else {
925 0 : fprintf(stderr, "%s\n",buf);
926 :
927 0 : if(!(target->ce_attr & A_PRECIOUS)||(target->ce_attr & A_ERRREMOVE))
928 0 : if( Remove_file( target->ce_fname ) == 0 )
929 0 : fprintf(stderr,"%s: '%s' removed.\n", Pname,
930 : target->ce_fname);
931 :
932 0 : Quit(0);
933 : }
934 : }
935 0 : else if(!(target->ce_attr & A_PRECIOUS)||(target->ce_attr & A_ERRREMOVE))
936 0 : Remove_file( target->ce_fname );
937 : }
938 2184 : }
939 :
940 :
941 : PUBLIC void
942 1858 : Update_time_stamp( cp )/*
943 : =========================
944 : Update the time stamp of cp and scan the list of its prerequisites for
945 : files being marked as removable (ie. an inferred intermediate node).
946 : Remove them if there are any. */
947 : CELLPTR cp;
948 : {
949 : HASHPTR hp;
950 : LINKPTR dp;
951 : CELLPTR tcp;
952 : time_t mintime;
953 1858 : int phony = ((cp->ce_attr&A_PHONY) != 0);
954 :
955 3716 : for(dp=CeMeToo(cp); dp; dp=dp->cl_next) {
956 1858 : tcp=dp->cl_prq;
957 : /* When calling Make() on this target ce_time was set to the minimal
958 : * required time the target should have after building, i.e. the time
959 : * stamp of the newest prerequisite or 1L if there is no
960 : * prerequisite. */
961 1858 : mintime = tcp->ce_time;
962 :
963 1858 : if( tcp->ce_attr & A_LIBRARY )
964 0 : Void_lib_cache( tcp->ce_fname, NIL(char) );
965 1858 : else if( !Touch && (tcp->ce_attr & A_LIBRARYM) )
966 0 : Void_lib_cache( tcp->ce_lib, tcp->ce_fname );
967 :
968 : /* phony targets are treated as if they were recently made
969 : * and get the current time assigned. */
970 1858 : if( phony ) {
971 1316 : tcp->ce_time = Do_time();
972 : }
973 542 : else if (Trace) {
974 0 : tcp->ce_time = Do_time();
975 : }
976 : else {
977 542 : Stat_target(tcp, -1, TRUE);
978 :
979 542 : if( tcp->ce_time == (time_t) 0L ) {
980 : /* If the target does not exist after building set its
981 : * time stamp depending if it has recipes or not. Virtual
982 : * Targets (without recipes) get the newest time stamp of
983 : * its prerequisites assigned. (This was conveniently stored
984 : * in mintime.)
985 : * Targets with recipes are treated as if they were recently
986 : * made and get the current time assigned. */
987 542 : if( cp->ce_recipe == NIL(STRING) && mintime > 1 ) {
988 186 : tcp->ce_time = mintime;
989 : }
990 : else {
991 356 : tcp->ce_time = Do_time();
992 : }
993 : }
994 : else {
995 : /* The target exist. If the target does not have recipe
996 : * lines use the newest time stamp of either the target or
997 : * the newest time stamp of its prerequisites and issue
998 : * a warning. */
999 0 : if( cp->ce_recipe == NIL(STRING) ) {
1000 0 : time_t newtime = ( mintime > 1 ? mintime : Do_time() );
1001 :
1002 0 : if( !(tcp->ce_attr & A_SILENT) )
1003 0 : Warning( "Found file corresponding to virtual target [%s].",
1004 0 : tcp->CE_NAME );
1005 :
1006 0 : if( newtime > tcp->ce_time )
1007 0 : tcp->ce_time = mintime;
1008 : }
1009 : }
1010 : }
1011 :
1012 1858 : if( Trace ) {
1013 0 : tcp->ce_flag |= F_STAT; /* pretend we stated ok */
1014 : }
1015 :
1016 1858 : if( Verbose & V_MAKE )
1017 0 : printf( "%s: <<<< Set [%s] time stamp to %lu\n",
1018 0 : Pname, tcp->CE_NAME, tcp->ce_time );
1019 :
1020 1858 : if( Measure & M_TARGET )
1021 0 : Do_profile_output( "e", M_TARGET, tcp );
1022 :
1023 : /* At this point cp->ce_time is updated to either the actual file
1024 : * time or the current time. */
1025 : DB_PRINT( "make", ("time stamp: %ld, required mintime: %ld",
1026 : cp->ce_time, mintime) );
1027 1858 : if( tcp->ce_time < mintime && !(tcp->ce_attr & A_SILENT) ) {
1028 0 : Warning( "Target [%s] was made but the time stamp has not been updated.",
1029 0 : tcp->CE_NAME );
1030 : }
1031 :
1032 : /* The target was made, remove the temp files now. */
1033 1858 : Unlink_temp_files( tcp );
1034 1858 : tcp->ce_flag |= F_MADE;
1035 1858 : tcp->ce_attr |= A_UPDATED;
1036 : }
1037 :
1038 : /* Scan the list of prerequisites and if we find one that is
1039 : * marked as being removable, (ie. an inferred intermediate node)
1040 : * then remove it. We remove a prerequisite by running the recipe
1041 : * associated with the special target .REMOVE.
1042 : * Typically .REMOVE is defined in the startup file as:
1043 : * .REMOVE :; $(RM) $<
1044 : * with $< being the list of prerequisites specified in the current
1045 : * target. (Make() sets $< .) */
1046 :
1047 : /* Make sure we don't try to remove prerequisites for the .REMOVE
1048 : * target. */
1049 1858 : if( strcmp(cp->CE_NAME,".REMOVE") != 0 &&
1050 : (hp = Get_name(".REMOVE", Defs, FALSE)) != NIL(HASH) ) {
1051 : register LINKPTR dp;
1052 1858 : int flag = FALSE;
1053 : int rem;
1054 : t_attr attr;
1055 :
1056 1858 : tcp = hp->CP_OWNR;
1057 :
1058 : /* The .REMOVE target is re-used. Remove old prerequisites. */
1059 1858 : tcp->ce_flag |= F_TARGET;
1060 1858 : Clear_prerequisites( tcp );
1061 :
1062 4052 : for(dp=cp->ce_prq; dp != NIL(LINK); dp=dp->cl_next) {
1063 2194 : register CELLPTR prq = dp->cl_prq;
1064 :
1065 2194 : attr = Glob_attr | prq->ce_attr;
1066 : /* We seem to have problems here that F_MULTI subtargets get removed
1067 : * that even though they are still needed because the A_PRECIOUS
1068 : * was not propagated correctly. Solution: Don't remove subtargets, the
1069 : * master target will be removed if is not needed. */
1070 4392 : rem = (prq->ce_flag & F_REMOVE) &&
1071 8 : (prq->ce_flag & F_MADE ) &&
1072 8 : !(prq->ce_count ) && /* Don't remove F_MULTI subtargets. */
1073 2202 : !(prq->ce_attr & A_PHONY) &&
1074 4 : !(attr & A_PRECIOUS);
1075 :
1076 : /* remove if rem is != 0 */
1077 2194 : if(rem) {
1078 : LINKPTR tdp;
1079 :
1080 : /* Add the target plus all that are linked to it with the .UPDATEALL
1081 : * attribute. */
1082 0 : for(tdp=CeMeToo(prq); tdp; tdp=tdp->cl_next) {
1083 0 : CELLPTR tmpcell=tdp->cl_prq;
1084 :
1085 0 : (Add_prerequisite(tcp,tmpcell,FALSE,FALSE))->cl_flag|=F_TARGET;
1086 0 : tmpcell->ce_flag &= ~F_REMOVE;
1087 : }
1088 0 : flag = TRUE;
1089 : }
1090 : }
1091 :
1092 1858 : if( flag ) {
1093 0 : int sv_force = Force;
1094 :
1095 0 : Force = FALSE;
1096 0 : Remove_prq( tcp );
1097 0 : Force = sv_force;
1098 :
1099 0 : for(dp=tcp->ce_prq; dp != NIL(LINK); dp=dp->cl_next) {
1100 0 : register CELLPTR prq = dp->cl_prq;
1101 :
1102 0 : prq->ce_flag &= ~(F_MADE|F_VISITED|F_STAT);
1103 0 : prq->ce_flag |= F_REMOVE;
1104 0 : prq->ce_time = (time_t)0L;
1105 : }
1106 : }
1107 : }
1108 1858 : }
1109 :
1110 :
1111 : PUBLIC int
1112 746 : Remove_file( name )
1113 : char *name;
1114 : {
1115 : struct stat buf;
1116 :
1117 746 : if( stat(name, &buf) != 0 )
1118 0 : return 1;
1119 746 : if( (buf.st_mode & S_IFMT) == S_IFDIR )
1120 0 : return 1;
1121 746 : return(unlink(name));
1122 : }
1123 :
1124 :
1125 : #if defined(__CYGWIN__)
1126 : char *
1127 : cygdospath(char *src, int winpath)/*
1128 : ====================================
1129 : Convert to DOS path if winpath is true. The returned pointer is
1130 : either the original pointer or a pointer to a static buffer.
1131 : */
1132 : {
1133 : static char *buf = NIL(char);
1134 :
1135 : if ( !buf && ( (buf = MALLOC( PATH_MAX, char)) == NIL(char) ) )
1136 : No_ram();
1137 :
1138 : DB_PRINT( "cygdospath", ("converting [%s] with winpath [%d]", src, winpath ) );
1139 :
1140 : /* Return immediately on NULL pointer or when .WINPATH is
1141 : * not set. */
1142 : if( !src || !winpath )
1143 : return src;
1144 :
1145 : if( *src && src[0] == '/' ) {
1146 : char *tmp;
1147 : int err = cygwin_conv_to_win32_path(src, buf);
1148 : if (err)
1149 : Fatal( "error converting \"%s\" - %s\n",
1150 : src, strerror (errno));
1151 :
1152 : tmp = buf;
1153 : while ((tmp = strchr (tmp, '\\')) != NULL) {
1154 : *tmp = '/';
1155 : tmp++;
1156 : }
1157 :
1158 : return buf;
1159 : }
1160 : else
1161 : return src;
1162 : }
1163 : #endif
|