-
Notifications
You must be signed in to change notification settings - Fork 348
/
otoolr.c
103 lines (93 loc) · 3.33 KB
/
otoolr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* Recursively find all shared libraries referenced by the executables and
* shared libraries given on the command line.
* The output list must be run through sort -u to remove duplicates.
* An example command line might be
*
* otoolr <builddir> /opt/local/bin/ffmpeg /opt/local/bin/gm ... | sort -u > t.lis
*
* The <builddir> is prepended if any of the executables or dylib files have relative paths,
* otherwise not used.
*
* P. Wessel, August 2020
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
struct LINK {
/* Struct with shared library name and pointer to next */
char name[256];
struct LINK *next;
};
int used (struct LINK *list, char *name) {
/* Determine if this library has already been processed */
struct LINK *this = list;
while (this && strcmp (name, this->name))
this = this->next;
return (this ? 1 : 0);
}
struct LINK * last (struct LINK *list) {
/* Find the last link in the chain so we can append to it */
struct LINK *this = list;
while (this->next)
this = this->next;
return (this);
}
void printlist (struct LINK *list) {
/* March through and print the entries except anything in /Users, free up along the way */
struct LINK *this = list, *prt;
while (this->next) {
prt = this->next;
if (!strstr (prt->name, "/Users")) printf ("%s\n", prt->name);
free (this);
this = prt;
}
}
/* Recursive function. Set verb = 1 for debugging */
void get_list (char *top, char *name, int level, struct LINK *list) {
/* Recursive scanner of entries returned by otool -L */
char process[256] = {""}, next[256] = {""}, line[256] = {""}, dir[256] = {""}, *c = NULL;
int k, library = (strstr (name, ".dylib") != NULL), verb = 0;
FILE *fp = NULL;
struct LINK *this = NULL;
strcpy (dir, top);
c = strrchr (dir, '/'); c[0] = '\0';
if (name[0] == '/') /* Full path */
sprintf (process, "otool -L %s", name);
else if (library) /* In the build dirs path */
sprintf (process, "otool -L %s/%s", dir, name);
else /* Executable */ /* In the bin dir */
sprintf (process, "otool -L %s/bin/%s", top, name);
if (verb) fprintf (stderr, "[%d-%d] Running: %s\n", level, library, process);
if ((fp = popen (process, "r")) == NULL) return;
fgets (line, 256U, fp); /* Skip header record */
while (fgets (line, 256U, fp)) {
k = 0; while (isspace (line[k])) k++; /* Scan to start of library name */
sscanf (&line[k], "%s", next); /* Get the shared library file name */
if (strstr (line, "/usr/lib") || strstr (line, "/System/Library") || (library && strstr (line, name))) {
/* Skip system files or itself */
if (verb) fprintf (stderr, "[%d-%d] Skip: %s\n", level, library, next);
continue;
}
if (used (list, next)) { /* Already processed this library */
if (verb) fprintf (stderr, "[%d-%d] Used: %s\n", level, library, next);
continue;
}
if (next[0] == '/') {
if (verb) fprintf (stderr, "[%d] Test: %s\n", level, next);
this = last (list);
this->next = calloc (1, sizeof (struct LINK));
strcpy (this->next->name, next);
get_list (top, next, level + 1, list);
}
}
pclose (fp);
}
int main (int argc, char **argv) {
int k;
struct LINK *list = calloc (1, sizeof (struct LINK));
strcpy (list->name, "HEAD"); /* Unused head of the linked list */
for (k = 2; k < argc; k++) /* Try all the arguments (argv[1] is the builddir) */
get_list (argv[1], argv[k], 0, list);
printlist (list);
}