]> rtime.felk.cvut.cz Git - l4.git/blob - l4/tool/gendep/deptrack.c
1a6bdc77adcf124a1a7ff52b9dc35c2bcc0d6bf5
[l4.git] / l4 / tool / gendep / deptrack.c
1 /*
2   deptrack.c -- high-level routines for gendepend.
3
4   (c) Han-Wen Nienhuys <hanwen@cs.uu.nl> 1998
5   (c) Michael Roitzsch <mroi@os.inf.tu-dresden.de> 2007
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <regex.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <assert.h>
15
16 #ifdef __APPLE__
17 #include <sys/types.h>
18 #include <sys/sysctl.h>
19 #include <unistd.h>
20 #endif
21
22 #include "gendep.h"
23
24 #ifndef min
25 #define min(a,b) ((a)<(b)?(a):(b))
26 #endif
27
28 extern int errno;
29
30 static FILE * output;
31 static struct
32 {
33   regex_t *re_array;
34   char *invert_array;
35   int max, no;
36 } regexps;
37
38 static char * executable_name;
39 static char * wanted_executable_name;
40 static char * target;
41 static char * depfile_name;
42
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)
46 then. */
47 static int detection_mode;
48 static int detection_done;
49
50 /* List of dependencies. We first collect all, then write them out. */
51 static struct strlist{
52         const char*name;
53         struct strlist *next;
54 } *dependencies; 
55
56 #define STRLEN 1024
57
58 /*
59   simplistic wordwrap
60  */
61 static void
62 write_word (char const * word)
63 {
64   static int column;
65   int l = strlen (word);
66   if (l + column > 75 && column>1)
67     {
68       fputs ("\\\n\t",output);
69       column = 8;
70     }
71
72   column += l;
73   fputs (word, output);
74   fputs (" ", output);
75 }
76
77 static void
78 xnomem(void *p)
79 {
80   if (!p)
81     {
82       errno = ENOMEM;
83       perror ("libgendep.so");
84     }
85 }
86
87 static char *
88 xstrdup (char *s)
89 {
90   char *c = strdup (s);
91   xnomem (c);
92   return c;
93 }
94
95 static void *
96 xrealloc (void *s, int sz)
97 {
98   void *c = realloc (s, sz);
99   xnomem (c);
100   return c;
101 }
102
103 static void *
104 xmalloc (int sz)
105 {
106   void *c;
107   c = malloc (sz);
108   xnomem (c);
109   return c;
110 }
111
112 /*!\brief Trim the filename
113  *
114  * Remove redundant "./" at the beginning of a filename.
115  *
116  * We are not very minimal about memory usage, because this would require
117  * scanning the filename twice, instead of once.
118  */
119 static char* trim (const char*fn){
120   char *name;
121
122   /* remove trailing "./" */
123   while(fn[0]=='.' && fn[1]=='/') fn+=2;
124   name = xmalloc(strlen(fn)+1);
125   if(name==0) return 0;
126
127   strcpy(name, fn);
128   return name;
129 }
130
131 /*!\brief add a dependency
132  *
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.
135  */
136 static void gendep__adddep(const char*name){
137   struct strlist *dep;
138   char *namecopy;
139
140   if((namecopy = trim(name))==0) return;
141
142   /* special detection mode ? */
143   if(detection_mode){
144     if(!strcmp(namecopy, target)){
145       detection_done=1;
146     }
147     free(namecopy);
148     return;
149   }
150   if(!strcmp(target, name)) return;
151   for(dep=dependencies; dep; dep=dep->next){
152         if(!strcmp(dep->name, namecopy)){
153             free(namecopy);
154             return;
155         }
156   }
157   dep = xmalloc(sizeof(struct strlist));
158   dep->name = namecopy;
159   dep->next = dependencies;
160   dependencies = dep;
161 }
162
163 /*!\brief Delete a dependency
164  *
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.
167  */
168 static void gendep__deldep(const char*name){
169   struct strlist *dep, *old;
170   char *namecopy;
171   
172   old = 0;
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;
178             free(namecopy);
179             free((char*)dep->name);
180             free(dep);
181             return;
182         }
183         old = dep;
184   }
185   free(namecopy);
186 }
187
188 static int
189 gendep_getenv (char **dest, char *what)
190 {
191   char var[STRLEN]="GENDEP_";
192   strcat (var, what);
193   *dest = getenv (var);
194
195   return !! (*dest);
196 }
197
198 /*
199   Do hairy stuff with regexps and environment variables.
200  */
201 static void
202 setup_regexps (void)
203 {
204   char *regexp_val =0;
205   char *end, *start;
206
207   if(gendep_getenv(&target, "SOURCE")){
208     /* we have to watch for the binary that opens our file first */
209     detection_mode=1;
210     gendep_getenv(&depfile_name, "OUTPUT");
211     return;
212   }
213
214   /* standard mode of operation: catch dependencies */
215   
216   if (!gendep_getenv (&wanted_executable_name, "BINARY"))
217     return;
218
219   if (strcmp (wanted_executable_name, executable_name))
220     {
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))
224         return;
225     }
226
227   gendep_getenv(&depfile_name, "DEPFILE");
228
229   if (!gendep_getenv (&target, "TARGET"))
230     return;
231
232   if (!gendep_getenv (&regexp_val, executable_name))
233     return;
234
235   /*
236     let's hope malloc (0) does the sensible thing (not return NULL, that is)
237   */
238   regexps.re_array = xmalloc (0);
239   regexps.invert_array = xmalloc (0);
240   
241   regexp_val = xstrdup (regexp_val);
242   start = regexp_val;
243   end = regexp_val + strlen (regexp_val);
244
245   /*
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. 
249    */
250   do {
251     char * end_re = 0;
252     int in_out;
253     while (*start && *start != '+' && *start != '-')
254       start ++;
255
256     in_out= (*start == '+') ;
257     start ++;
258     end_re = strchr (start, ' ');
259     if (end_re)
260       *end_re = 0;
261
262     if (*start)
263       {
264         regex_t regex;
265         int result= regcomp (&regex, start, REG_NOSUB);
266
267         if (result)
268           {
269             fprintf (stderr, "libgendep.so: Bad regular expression `%s\'\n", start);
270             goto duh;           /* ugh */
271           }
272
273         if (regexps.no >= regexps.max)
274           {
275             /* max_regexps = max_regexps * 2 + 1; */
276             regexps.max ++;
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));        
279           }
280
281         regexps.re_array[regexps.no] = regex;
282         regexps.invert_array[regexps.no++] = in_out;
283       }
284
285   duh:
286     start = end_re ? end_re + 1 :  end;
287   } while (start < end);
288 }
289
290 /*
291   Try to get the name of the binary.  Is there a portable way to do this?
292  */
293 static void get_executable_name (void)
294 {
295   char *basename_p;
296 #ifdef __linux
297   const char * const proc_cmdline = "/proc/self/cmdline";
298   FILE *cmdline = fopen (proc_cmdline, "r");
299   char cmd[STRLEN];
300   int i=0;
301   int c;
302   cmd[STRLEN-1] = 0;
303  
304   if (!cmdline)
305     {
306       fprintf(stderr, "libgendep.o: cannot open %s\n", proc_cmdline);
307       exit(-1);
308     }
309       
310   while ((c = fgetc(cmdline))!=EOF && c && i < STRLEN-1)
311     cmd[i++] = c;
312
313   cmd[i++] = 0;
314 #elif defined(__APPLE__)
315   int mib[3], arglen;
316   size_t size;
317   char *procargs, *cmd;
318   
319   /* allocate process argument space */
320   mib[0] = CTL_KERN;
321   mib[1] = KERN_ARGMAX;
322   size = sizeof(arglen);
323   if (sysctl(mib, 2, &arglen, &size, NULL, 0) == -1)
324     exit(-1);
325   if (!(procargs = xmalloc(arglen)))
326     exit(-1);
327
328   /* get a copy of the process argument space */
329   mib[0] = CTL_KERN;
330   mib[1] = KERN_PROCARGS2;
331   mib[2] = getpid();
332   size = (size_t)arglen;
333   if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
334     free(procargs);
335     exit(-1);
336   }
337   
338   /* jump over the argument count */
339   cmd = procargs + sizeof(int);
340 #else
341 #  error "Retrieving the executable name is not implemented for your platform."
342 #endif
343   
344   /* ugh.  man 3 basename -> ?  */
345   basename_p =  strrchr (cmd, '/');
346   if (basename_p)
347     basename_p++;
348   else
349     basename_p = cmd;
350   
351   executable_name = xstrdup (basename_p);
352   
353 #ifdef __APPLE__
354   free(procargs);
355 #endif
356 }
357
358 static void initialize (void) __attribute__ ((constructor));
359
360 static void initialize (void)
361 {
362   get_executable_name ();
363   setup_regexps ();
364
365   if (target && !detection_mode)
366     {
367       char fn[STRLEN];
368       char *slash;
369
370       fn[0] = '\0';
371       if(depfile_name){
372           strncpy(fn, depfile_name, STRLEN);
373       } else {
374           slash = strrchr(target, '/');
375           /* copy the path */
376           strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
377           strncat (fn, ".", STRLEN);
378           /* copy the name */
379           strncat(fn, slash?slash+1:target, STRLEN);
380           strncat (fn, ".d", STRLEN);
381       }
382       fn[STRLEN-1]=0;
383       
384       if((output = fopen (fn, "w"))==0){
385         fprintf(stderr, "libgendep.so: cannot open %s\n", fn);
386         return;
387       }
388       write_word (target);
389       write_word (":");
390     }
391 }
392
393 /* Someone is opening a file.  If it is opened for reading, and
394   matches the regular expressions, write it to the dep-file. 
395   
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.
398  */
399 void
400 gendep__register_open (char const *fn, int flags)
401 {
402   if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
403     {
404       int i;
405
406       if (fn == strstr(fn, "/etc/mtab") ||
407           fn == strstr(fn, "/proc/"))
408         return;
409
410       for (i =0; i<  regexps.no; i++)
411         {
412           int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
413
414           if (!(not_matched ^ regexps.invert_array[i]))
415             return;
416         }
417
418       gendep__adddep(fn);
419     }
420 }
421
422 void
423 gendep__register_unlink (char const *fn)
424 {
425   gendep__deldep(fn);
426 }
427
428
429 static void finish (void) __attribute__ ((destructor));
430
431 static void finish (void)
432 {
433   if (output)
434     {
435       struct strlist *dep;
436
437       for(dep=dependencies; dep; dep=dep->next){
438         write_word(dep->name);
439       }
440       fprintf (output, "\n");
441       for(dep=dependencies; dep; dep=dep->next){
442         fputs(dep->name, output);
443         fputs(":\n", output);
444       }
445       fclose (output);
446       return;
447     }
448   
449   if(detection_mode && detection_done && depfile_name){
450     FILE *file;
451     
452     file = fopen(depfile_name, "w");
453     if(!file) return;
454     fwrite(executable_name, strlen(executable_name), 1, file);
455     fclose(file);
456     return;
457   }
458 }
459