2 deptrack.c -- high-level routines for gendepend.
4 (c) Han-Wen Nienhuys <hanwen@cs.uu.nl> 1998
5 (c) Michael Roitzsch <mroi@os.inf.tu-dresden.de> 2007
16 #if defined(__APPLE__) || defined(__FreeBSD__)
17 #include <sys/types.h>
18 #include <sys/sysctl.h>
25 #define min(a,b) ((a)<(b)?(a):(b))
38 static char * executable_name;
39 static char * wanted_executable_name;
41 static char * depfile_name;
43 /* Special mode: Detection mode. In this mode, we watch for a binary opening
44 a file specified in the environment variable GENDEP_SOURCE (stored in
45 target). The binary name is written into $(GENDEP_OUTPUT) (depfile_name)
47 static int detection_mode;
48 static int detection_done;
50 /* List of dependencies. We first collect all, then write them out. */
51 static struct strlist{
62 write_word (char const * word)
65 int l = strlen (word);
66 if (l + column > 75 && column>1)
68 fputs ("\\\n\t",output);
83 perror ("libgendep.so");
96 xrealloc (void *s, int sz)
98 void *c = realloc (s, sz);
112 /*!\brief Trim the filename
114 * Remove redundant "./" at the beginning of a filename.
116 * We are not very minimal about memory usage, because this would require
117 * scanning the filename twice, instead of once.
119 static char* trim (const char*fn){
122 /* remove trailing "./" */
123 while(fn[0]=='.' && fn[1]=='/') fn+=2;
124 name = xmalloc(strlen(fn)+1);
125 if(name==0) return 0;
131 /*!\brief add a dependency
133 * Add a dependency to the list of dependencies for the current target.
134 * If the name is already registered, it won't be added.
136 static void gendep__adddep(const char*name){
140 if((namecopy = trim(name))==0) return;
142 /* special detection mode ? */
144 if(!strcmp(namecopy, target)){
150 if(!strcmp(target, name)) return;
151 for(dep=dependencies; dep; dep=dep->next){
152 if(!strcmp(dep->name, namecopy)){
157 dep = xmalloc(sizeof(struct strlist));
158 dep->name = namecopy;
159 dep->next = dependencies;
163 /*!\brief Delete a dependency
165 * Delete a dependency from the list of dependencies for the current target.
166 * This is used if an already registered file is removed later.
168 static void gendep__deldep(const char*name){
169 struct strlist *dep, *old;
173 if((namecopy = trim(name))==0) return;
174 for(dep=dependencies; dep; dep=dep->next){
175 if(!strcmp(dep->name, namecopy)){
176 if(old) old->next = dep->next;
177 else dependencies=dep->next;
179 free((char*)dep->name);
189 gendep_getenv (char **dest, char *what)
191 char var[STRLEN]="GENDEP_";
193 *dest = getenv (var);
199 Do hairy stuff with regexps and environment variables.
207 if(gendep_getenv(&target, "SOURCE")){
208 /* we have to watch for the binary that opens our file first */
210 gendep_getenv(&depfile_name, "OUTPUT");
214 /* standard mode of operation: catch dependencies */
216 if (!gendep_getenv (&wanted_executable_name, "BINARY"))
219 if (strcmp (wanted_executable_name, executable_name))
221 /* support two binary names (e.g. for arch-foo-X and X) */
222 if (!gendep_getenv (&wanted_executable_name, "BINARY_ALT1")
223 || strcmp (wanted_executable_name, executable_name))
227 gendep_getenv(&depfile_name, "DEPFILE");
229 if (!gendep_getenv (&target, "TARGET"))
232 if (!gendep_getenv (®exp_val, executable_name))
236 let's hope malloc (0) does the sensible thing (not return NULL, that is)
238 regexps.re_array = xmalloc (0);
239 regexps.invert_array = xmalloc (0);
241 regexp_val = xstrdup (regexp_val);
243 end = regexp_val + strlen (regexp_val);
246 Duh. The strength of C : string handling. (not). Pull apart the whitespace delimited
247 list of regexps, and try to compile them.
248 Be really gnuish with dynamic allocation of arrays.
253 while (*start && *start != '+' && *start != '-')
256 in_out= (*start == '+') ;
258 end_re = strchr (start, ' ');
265 int result= regcomp (®ex, start, REG_NOSUB);
269 fprintf (stderr, "libgendep.so: Bad regular expression `%s\'\n", start);
273 if (regexps.no >= regexps.max)
275 /* max_regexps = max_regexps * 2 + 1; */
277 regexps.re_array = xrealloc (regexps.re_array, regexps.max * sizeof (regex_t));
278 regexps.invert_array = xrealloc (regexps.invert_array, regexps.max * sizeof (char));
281 regexps.re_array[regexps.no] = regex;
282 regexps.invert_array[regexps.no++] = in_out;
286 start = end_re ? end_re + 1 : end;
287 } while (start < end);
291 Try to get the name of the binary. Is there a portable way to do this?
293 static void get_executable_name (void)
297 const char * const proc_cmdline = "/proc/self/cmdline";
298 FILE *cmdline = fopen (proc_cmdline, "r");
306 fprintf(stderr, "libgendep.o: cannot open %s\n", proc_cmdline);
310 while ((c = fgetc(cmdline))!=EOF && c && i < STRLEN-1)
314 #elif defined(__APPLE__) || defined(__FreeBSD__)
317 char *procargs, *cmd;
319 /* allocate process argument space */
321 mib[1] = KERN_ARGMAX;
322 size = sizeof(arglen);
323 if (sysctl(mib, 2, &arglen, &size, NULL, 0) == -1)
325 if (!(procargs = xmalloc(arglen)))
328 /* get a copy of the process argument space */
330 #if defined(__APPLE__)
331 mib[1] = KERN_PROCARGS2;
333 size = (size_t)arglen;
334 if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
340 mib[2] = KERN_PROC_ARGS;
342 size = (size_t)arglen;
343 if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
349 /* jump over the argument count */
350 cmd = procargs + sizeof(int);
352 # error "Retrieving the executable name is not implemented for your platform."
355 /* ugh. man 3 basename -> ? */
356 basename_p = strrchr (cmd, '/');
362 executable_name = xstrdup (basename_p);
364 #if defined(__APPLE__) || defined(__FreeBSD__)
369 static void initialize (void) __attribute__ ((constructor));
371 static void initialize (void)
373 get_executable_name ();
376 if (target && !detection_mode)
383 strncpy(fn, depfile_name, STRLEN);
385 slash = strrchr(target, '/');
387 strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
388 strncat (fn, ".", STRLEN);
390 strncat(fn, slash?slash+1:target, STRLEN);
391 strncat (fn, ".d", STRLEN);
395 if((output = fopen (fn, "w"))==0){
396 fprintf(stderr, "libgendep.so: cannot open %s\n", fn);
404 /* Someone is opening a file. If it is opened for reading, and
405 matches the regular expressions, write it to the dep-file.
407 Note that we explicitly ignore accesses to /etc/mtab and /proc/...
408 as these files are inspected by libc for Linux and tend to change.
411 gendep__register_open (char const *fn, int flags)
413 if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
417 if (fn == strstr(fn, "/etc/mtab") ||
418 fn == strstr(fn, "/proc/"))
421 for (i =0; i< regexps.no; i++)
423 int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
425 if (!(not_matched ^ regexps.invert_array[i]))
434 gendep__register_unlink (char const *fn)
440 static void finish (void) __attribute__ ((destructor));
442 static void finish (void)
448 for(dep=dependencies; dep; dep=dep->next){
449 write_word(dep->name);
451 fprintf (output, "\n");
452 for(dep=dependencies; dep; dep=dep->next){
453 fputs(dep->name, output);
454 fputs(":\n", output);
460 if(detection_mode && detection_done && depfile_name){
463 file = fopen(depfile_name, "w");
465 fwrite(executable_name, strlen(executable_name), 1, file);