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
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__)
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 mib[1] = KERN_PROCARGS2;
332 size = (size_t)arglen;
333 if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
338 /* jump over the argument count */
339 cmd = procargs + sizeof(int);
341 # error "Retrieving the executable name is not implemented for your platform."
344 /* ugh. man 3 basename -> ? */
345 basename_p = strrchr (cmd, '/');
351 executable_name = xstrdup (basename_p);
358 static void initialize (void) __attribute__ ((constructor));
360 static void initialize (void)
362 get_executable_name ();
365 if (target && !detection_mode)
372 strncpy(fn, depfile_name, STRLEN);
374 slash = strrchr(target, '/');
376 strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
377 strncat (fn, ".", STRLEN);
379 strncat(fn, slash?slash+1:target, STRLEN);
380 strncat (fn, ".d", STRLEN);
384 if((output = fopen (fn, "w"))==0){
385 fprintf(stderr, "libgendep.so: cannot open %s\n", fn);
393 /* Someone is opening a file. If it is opened for reading, and
394 matches the regular expressions, write it to the dep-file.
396 Note that we explicitly ignore accesses to /etc/mtab and /proc/...
397 as these files are inspected by libc for Linux and tend to change.
400 gendep__register_open (char const *fn, int flags)
402 if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
406 if (fn == strstr(fn, "/etc/mtab") ||
407 fn == strstr(fn, "/proc/"))
410 for (i =0; i< regexps.no; i++)
412 int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
414 if (!(not_matched ^ regexps.invert_array[i]))
423 gendep__register_unlink (char const *fn)
429 static void finish (void) __attribute__ ((destructor));
431 static void finish (void)
437 for(dep=dependencies; dep; dep=dep->next){
438 write_word(dep->name);
440 fprintf (output, "\n");
441 for(dep=dependencies; dep; dep=dep->next){
442 fputs(dep->name, output);
443 fputs(":\n", output);
449 if(detection_mode && detection_done && depfile_name){
452 file = fopen(depfile_name, "w");
454 fwrite(executable_name, strlen(executable_name), 1, file);