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