]> rtime.felk.cvut.cz Git - l4.git/blob - l4/tool/gendep/deptrack.c
update
[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 #if defined(__APPLE__) || defined(__FreeBSD__)
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__) || defined(__FreeBSD__)
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 #if defined(__APPLE__)
331   mib[1] = KERN_PROCARGS2;
332   mib[2] = getpid();
333   size = (size_t)arglen;
334   if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) {
335     free(procargs);
336     exit(-1);
337   }
338 #else
339   mib[1] = KERN_PROC;
340   mib[2] = KERN_PROC_ARGS;
341   mib[3] = getpid();
342   size = (size_t)arglen;
343   if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) {
344     free(procargs);
345     exit(-1);
346   }
347 #endif
348
349   /* jump over the argument count */
350   cmd = procargs + sizeof(int);
351 #else
352 #  error "Retrieving the executable name is not implemented for your platform."
353 #endif
354
355   /* ugh.  man 3 basename -> ?  */
356   basename_p =  strrchr (cmd, '/');
357   if (basename_p)
358     basename_p++;
359   else
360     basename_p = cmd;
361
362   executable_name = xstrdup (basename_p);
363
364 #if defined(__APPLE__) || defined(__FreeBSD__)
365   free(procargs);
366 #endif
367 }
368
369 static void initialize (void) __attribute__ ((constructor));
370
371 static void initialize (void)
372 {
373   get_executable_name ();
374   setup_regexps ();
375
376   if (target && !detection_mode)
377     {
378       char fn[STRLEN];
379       char *slash;
380
381       fn[0] = '\0';
382       if(depfile_name){
383           strncpy(fn, depfile_name, STRLEN);
384       } else {
385           slash = strrchr(target, '/');
386           /* copy the path */
387           strncat (fn, target, min(STRLEN, slash?slash-target+1:0));
388           strncat (fn, ".", STRLEN);
389           /* copy the name */
390           strncat(fn, slash?slash+1:target, STRLEN);
391           strncat (fn, ".d", STRLEN);
392       }
393       fn[STRLEN-1]=0;
394       
395       if((output = fopen (fn, "w"))==0){
396         fprintf(stderr, "libgendep.so: cannot open %s\n", fn);
397         return;
398       }
399       write_word (target);
400       write_word (":");
401     }
402 }
403
404 /* Someone is opening a file.  If it is opened for reading, and
405   matches the regular expressions, write it to the dep-file. 
406   
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.
409  */
410 void
411 gendep__register_open (char const *fn, int flags)
412 {
413   if ((detection_mode || output) && !(flags & (O_WRONLY | O_RDWR)))
414     {
415       int i;
416
417       if (fn == strstr(fn, "/etc/mtab") ||
418           fn == strstr(fn, "/proc/"))
419         return;
420
421       for (i =0; i<  regexps.no; i++)
422         {
423           int not_matched = regexec (regexps.re_array +i, fn, 0, NULL, 0);
424
425           if (!(not_matched ^ regexps.invert_array[i]))
426             return;
427         }
428
429       gendep__adddep(fn);
430     }
431 }
432
433 void
434 gendep__register_unlink (char const *fn)
435 {
436   gendep__deldep(fn);
437 }
438
439
440 static void finish (void) __attribute__ ((destructor));
441
442 static void finish (void)
443 {
444   if (output)
445     {
446       struct strlist *dep;
447
448       for(dep=dependencies; dep; dep=dep->next){
449         write_word(dep->name);
450       }
451       fprintf (output, "\n");
452       for(dep=dependencies; dep; dep=dep->next){
453         fputs(dep->name, output);
454         fputs(":\n", output);
455       }
456       fclose (output);
457       return;
458     }
459   
460   if(detection_mode && detection_done && depfile_name){
461     FILE *file;
462     
463     file = fopen(depfile_name, "w");
464     if(!file) return;
465     fwrite(executable_name, strlen(executable_name), 1, file);
466     fclose(file);
467     return;
468   }
469 }
470