tools-make/which_lib.c
Nicola Pero 92b3ea2f7e Check that the result of opendir is not NULL, to prevent crash for
not-existing library paths


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/tools/make/trunk@12103 72102866-910b-0410-8b05-ffd578937521
2002-01-14 22:12:01 +00:00

879 lines
20 KiB
C

/*
which_lib.c
Copyright (C) 1997, 2001, 2002 Free Software Foundation, Inc.
Author: Nicola Pero <nicola@brainstorm.co.uk>
Date: January 2002
Based on the original which_lib.c by Ovidiu Predescu,
Author: Ovidiu Predescu <ovidiu@net-community.com>
Date: October 1997
This file is part of the GNUstep Makefile Package.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
You should have received a copy of the GNU General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*
Command line arguments are:
* a list of library search paths in the GCC notation, as in
-L/usr/lib/opt/hack/
-L/usr/GNUstep/Local/Libraries/ix86/linux-gnu/gnu-gnu-gnu
-L/usr/GNUstep/System/Libraries/ix86/linux-gnu/gnu-gnu-gnu
(order is important, paths are searched in the order they are listed);
* a list of libraries in the GCC notation, as in
-lgnustep-base -lgnustep-gui -lobjc
* flags specifying whether a debug, profile, static/shared library is
to be preferred, as in
debug=no profile=yes shared=yes
The tool outputs the same list of library search paths and the list
of libraries it received in input, with an important modification:
each library name is modified to match the available version of the
library (by appending nothing for a normal library, _d for a debug
version of the library, _p for a profile one, _s for a static one,
and the various combinations, _dp, _ds, _ps, _dps) -- giving
preference to libraries matching the specified debug, profile,
shared library flags. For example, if a debug=yes profile=no
shared=yes is specified, and libgnustep-base_d.so.1.1.0 is in the
library search path, which_lib will replace -lgnustep-base with
-lgnustep-base_d in the output.
Here is exactly how the search is performed:
The tool first searches into the list of directories for a library
exactly matching the name and the type required. If found, it's
used.
If none is found, the library looks for an approximate match. Each
search in the following list is performed on the list of
directories, and uses the shared flags as specified.
If (debug=yes and profile=yes), the tool looks first for a debug=yes
profile=no, then a debug=no profile=yes.
If (debug=yes and profile=no), the tool looks for a debug=no
profile=no.
If (debug=no and profile=yes), the tool looks for a debug=no
profile=no.
If (debug=no and profile=no), the tool looks for a debug=yes
profile=no.
If none is still found, the tool looks for any available shared
library with that name (regardless of wheter it's
debug/profile/nothing), but only if shared=yes.
If none is still found, the tool looks for any available static
library with that name (regardless of any debug/profile/shared
flag).
If still not found, the tool outputs the unmodified library name (as
in -lgnustep-base) ... perhaps the library is somewhere else in the
linker path ... otherwise that will normally result in a linker
error later on.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#if HAVE_SYS_ERRNO_H
# include <sys/errno.h>
#endif
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#include <fcntl.h>
#if HAVE_DIRENT_H
# include <dirent.h>
#else
/* FIXME - is the following needed ? */
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
/* determine filesystem max path length */
# include <limits.h> /* for PATH_MAX */
#ifdef _POSIX_VERSION
# include <utime.h>
#else
# if HAVE_SYS_PARAM_H
# include <sys/param.h> /* for MAXPATHLEN */
# endif
#endif
#ifndef PATH_MAX
# ifdef _POSIX_VERSION
# define PATH_MAX _POSIX_PATH_MAX
# else
# ifdef MAXPATHLEN
# define PATH_MAX MAXPATHLEN
# else
# define PATH_MAX 1024
# endif
# endif
#endif
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#define PATH_SEP "/"
/* Extension used for shared and non-shared libraries. */
char* libext = ".a";
char* shared_libext = ".so";
/* If set to 1, all code will print out information about what it
does. */
int show_all = 0;
/* Strips off carriage returns, newlines and spaces at the end of the
string. (this removes some \r\n issues on Windows) */
static void stripstr (unsigned char *s)
{
unsigned len;
if (s == NULL)
{
return;
}
len = strlen (s);
while (len > 0)
{
len--;
if (s[len] < 33)
{
s[len] = '\0';
}
}
}
/* Search for a library with library_name and extension ext.
library_name must not contain the suffix, so library_name should
be something like 'gnustep-base'. If suffix == NULL, any library
with that name, regardless of the suffix (valid suffixes are: "",
_d, _p, _s, _ds, _dp, _ps, _dps) will be accepted. If suffix !=
NULL, the suffix must be that one, otherwise it's not considered a
match.
ext is the wanted extension (normally, either .so or .a). Must
not be NULL.
Return 0 if it doesn't find anything mathching.
Return 1 if a library with the appropriate extension/type matches
in 'path' and print to stdout the name of the library. */
int search_for_library_with_type (const char *library_name,
int library_name_len,
char **library_paths,
int paths_no,
char *suffix,
char *ext)
{
/* Iterate over the library_paths, looking for the library. */
int i, found = 0;
if (show_all)
{
fprintf (stderr, "Scanning all paths for '%s', suffix '%s', "
"extension '%s'\n ", library_name,
suffix ? suffix : "(null)",
ext);
}
for (i = 0; i < paths_no; i++)
{
DIR* dir;
struct dirent* dirbuf;
if (show_all)
{
fprintf (stderr, " Path: %s\n", library_paths[i]);
}
dir = opendir (library_paths[i]);
if (dir == NULL)
{
/* For some reasons, we can't read that path. */
continue;
}
while ((dirbuf = readdir (dir)))
{
/* Skip if it doesn't begin with 'lib'. This implicitly
skips "." and ".." in case they are returned. */
if (dirbuf->d_name[0] != 'l')
{
continue;
}
if (dirbuf->d_name[1] != 'i')
{
continue;
}
if (dirbuf->d_name[2] != 'b')
{
continue;
}
/* Skip if it does not match the library name. */
if (strncmp (dirbuf->d_name + 3, library_name, library_name_len))
{
continue;
}
else
{
int filelen, extlen;
filelen = strlen (dirbuf->d_name);
extlen = strlen (ext);
if (filelen - extlen <= 0)
{
/* Quite worrying this case. */
continue;
}
if (show_all)
{
fprintf (stderr, " Considering %s\n", dirbuf->d_name);
}
/* First check if the extension matches */
if (strcmp (dirbuf->d_name + filelen - extlen, ext))
{
/* No luck, skip this file */
continue;
}
/* The extension matches. Check the last remaining bit
- that the remaining string we have not checked is
the required suffix, or one of the allowed suffixes
if we have no required suffix. The allowed suffixes
are: "", _d, _p, _s, _dp, _ds, _ps, _dps. */
{
int suffix_len = filelen - (3 /* 'lib' */
+ library_name_len
/* library_name */
+ extlen /* .so/.a */);
if (suffix != NULL)
{
if (suffix_len == strlen (suffix))
{
if (suffix_len != 0
&& strncmp (dirbuf->d_name + 3 + library_name_len,
suffix, suffix_len))
{
continue;
}
}
else
{
continue;
}
}
else
{
switch (suffix_len)
{
case 0:
{
/* nothing - it's Ok. */
found = 1;
break;
}
case 1:
{
continue;
}
case 2:
{
/* Must be one of _d, _p, _s */
char c;
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
c = dirbuf->d_name[3 + library_name_len + 1];
if (c != 'd' || c != 'p' || c != 's')
{
continue;
}
found = 1;
break;
}
case 3:
{
/* Must be one of _dp, _ds, _ps */
char c, d;
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
c = dirbuf->d_name[3 + library_name_len + 1];
d = dirbuf->d_name[3 + library_name_len + 2];
if ((c == 'd' && (d == 'p' || d == 's'))
|| (c == 'p' && d == 's'))
{
found = 1;
break;
}
continue;
}
case 4:
{
if (dirbuf->d_name[3 + library_name_len] != '_')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 'd')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 'p')
{
continue;
}
if (dirbuf->d_name[3 + library_name_len] != 's')
{
continue;
}
found = 1;
break;
}
default:
{
continue;
}
}
}
/* If we're here, it's because it was found! */
{
char filename[PATH_MAX + 1];
if (show_all)
{
fprintf (stderr, " Found!\n");
}
/* Copy the name of the library without the "lib"
prefix and the extension. */
strncpy (filename, dirbuf->d_name + 3,
filelen - extlen - 3);
filename[filelen - extlen - 3] = 0;
printf (" -l%s", filename);
found = 1;
break;
}
}
}
}
closedir (dir);
if (found)
{
return 1;
}
}
return 0;
}
/* Search for the library everywhere, and returns the library name. */
void output_library_name (const char *library_name,
char** library_paths, int paths_no,
int debug, int profile, int shared)
{
int library_name_len = strlen (library_name);
/* We first perform the search of a matching library in all dirs. */
int i;
char libname_suffix[5];
/* Setup the exact prefix of the library we are looking for */
libname_suffix[0] = '_';
libname_suffix[1] = '\0';
libname_suffix[2] = '\0';
libname_suffix[3] = '\0';
libname_suffix[4] = '\0';
i = 1;
if (debug)
{
libname_suffix[i] = 'd';
i++;
}
if (profile)
{
libname_suffix[i] = 'p';
i++;
}
if (!shared)
{
libname_suffix[i] = 's';
i++;
}
if (i == 1)
{
libname_suffix[0] = '\0';
}
if (show_all)
{
fprintf (stderr, "\n== Searching for library %s: ==\n", library_name);
}
if (show_all)
{
fprintf (stderr, "Scanning all paths for an exact match - "
"libname_suffix='%s'\n", libname_suffix);
}
for (i = 0; i < paths_no; i++)
{
struct stat statbuf;
char full_filename[PATH_MAX + 1];
if (show_all)
{
fprintf (stderr, " Path:%s\n", library_paths[i]);
}
if (stat (library_paths[i], &statbuf) < 0)
/* Error occured or dir doesn't exist */
{
if (show_all)
{
fprintf (stderr, " Could not read directory\n");
}
continue;
}
else if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
/* Not a directory */
{
if (show_all)
{
fprintf (stderr, " Directory is not a directory.\n");
}
continue;
}
strcpy (full_filename, library_paths[i]);
strcat (full_filename, PATH_SEP);
strcat (full_filename, "lib");
strcat (full_filename, library_name);
strcat (full_filename, libname_suffix);
strcat (full_filename, shared ? shared_libext : libext);
if (show_all)
{
fprintf (stderr, " Try %s\n", full_filename);
}
if (stat (full_filename, &statbuf) < 0 && errno != ENOENT)
/* Error */
{
if (show_all)
{
fprintf (stderr, " Could not read file\n");
}
continue;
}
if ((statbuf.st_mode & S_IFMT) == S_IFREG)
/* Found it! */
{
if (show_all)
{
fprintf (stderr, " Found!\n");
}
printf (" -l%s", library_name);
if (*libname_suffix)
{
printf ("%s", libname_suffix);
}
return;
}
}
/* The library was not found. Try various approximations first,
slightly messing the original debug/profile requests, but still
honouring the shared=yes|no requirement. */
{
char *extension = shared ? shared_libext : libext;
/* _dp case: try _d, then _p */
if (debug && profile)
{
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
shared ? "_d" : "_ds", extension))
{
return;
}
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
shared ? "_p" : "_ps", extension))
{
return;
}
}
/* _d or _p: try nothing. */
if ((debug && !profile) || (!debug && profile))
{
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
shared ? "" : "_s", extension))
{
return;
}
}
/* nothing: try _d. */
if (!debug && !profile)
{
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
shared ? "_d" : "_ds", extension))
{
return;
}
}
}
/* The library was still not found. Try to get whatever library we
have there. */
/* If a shared library is needed try to find a shared one first.
Any shared library is all right. */
if (shared)
{
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
NULL, shared_libext))
{
return;
}
}
/* Last hope - return a static library with name 'library_name'.
Any static library is all right. */
if (search_for_library_with_type (library_name, library_name_len,
library_paths, paths_no,
NULL, libext))
{
return;
}
/* We couldn't locate the library. Output the library name we were
given, without any modification. Possibly it's somewhere else on
the linker path, otherwise (more likely) a linker error will
occur. Nothing we can do about it. */
if (show_all)
{
fprintf (stderr, " Library not found, using unmodified name\n");
}
printf (" -l%s", library_name);
return;
}
int main (int argc, char** argv)
{
/* Type of libraries we prefer. */
int debug = 0;
int profile = 0;
int shared = 1;
/* Array of strings that are the library paths passed on the command
line. */
int paths_no = 0;
char** library_paths = NULL;
/* The same library paths, but converted to a format that Windows
functions (if on Windows) understand, so that you should pass
those to Windows functions accessing the filesystem. If you're
not on Windows, there are the same as the library_paths. */
char **normalized_library_paths = NULL;
/* The list of libraries */
int libraries_no = 0;
char** all_libraries = NULL;
/* Other flags which are printed to the output as they are. */
int other_flags_no = 0;
char** other_flags = NULL;
#ifdef __WIN32__
setmode(1, O_BINARY);
setmode(2, O_BINARY);
#endif
if (argc == 1)
{
printf ("usage: %s [-Lpath ...] -llibrary shared=yes|no debug=yes|no "
"profile=yes|no libext=string shared_libext=string "
"[show_all=yes]\n", argv[0]);
exit (1);
}
{
int i;
for (i = 1; i < argc; i++)
{
/* Switch basing on the first letter of each argument, to reduce the
number of comparisons needed. */
switch (argv[i][0])
{
case '-':
{
if (argv[i][1] == 'l')
{
if (all_libraries)
{
all_libraries = realloc (all_libraries,
(libraries_no + 1)
* sizeof (char*));
}
else
{
all_libraries = malloc ((libraries_no + 1)
* sizeof (char*));
}
all_libraries[libraries_no] = malloc (strlen (argv[i]) - 1);
strcpy (all_libraries[libraries_no], argv[i] + 2);
stripstr (all_libraries[libraries_no]);
libraries_no++;
continue;
}
else if (argv[i][1] == 'L')
{
if (library_paths)
{
library_paths = realloc (library_paths, (paths_no + 1)
* sizeof (char*));
}
else
{
library_paths = malloc ((paths_no + 1) * sizeof(char*));
}
library_paths[paths_no] = malloc (strlen (argv[i]) - 1);
strcpy (library_paths[paths_no], argv[i] + 2);
stripstr (library_paths[paths_no]);
paths_no++;
continue;
}
break;
}
case 'd':
{
if (!strncmp (argv[i], "debug=", 6))
{
debug = !strncmp (argv[i] + 6, "yes", 3);
continue;
}
break;
}
case 'l':
{
if (!strncmp (argv[i], "libext=", 7))
{
libext = malloc (strlen (argv[i] + 7) + 1);
strcpy (libext, argv[i] + 7);
continue;
}
break;
}
case 'p':
{
if (!strncmp (argv[i], "profile=", 8))
{
profile = !strncmp (argv[i] + 8, "yes", 3);
continue;
}
break;
}
case 's':
{
if (!strncmp (argv[i], "shared=", 7))
{
shared = !strncmp (argv[i] + 7, "yes", 3);
continue;
}
else if (!strncmp (argv[i], "shared_libext=", 14))
{
shared_libext = malloc (strlen (argv[i] + 14) + 1);
strcpy (shared_libext, argv[i] + 14);
continue;
}
else if (!strncmp (argv[i], "show_all=", 9))
{
show_all = !strncmp (argv[i] + 9, "yes", 3);
continue;
}
break;
}
default:
break;
}
/* The flag is something different; keep it in the `other_flags' */
if (other_flags)
{
other_flags = realloc (other_flags,
(other_flags_no + 1) * sizeof (char*));
}
else
{
other_flags = malloc ((other_flags_no + 1) * sizeof (char*));
}
other_flags[other_flags_no] = malloc (strlen (argv[i]) + 1);
strcpy (other_flags[other_flags_no], argv[i]);
other_flags_no++;
}
}
if (show_all)
{
int i;
fprintf (stderr, "== Input: ==\n");
fprintf (stderr, "shared = %d\n", shared);
fprintf (stderr, "debug = %d\n", debug);
fprintf (stderr, "profile = %d\n", profile);
fprintf (stderr, "libext = %s\n", libext);
fprintf (stderr, "shared_libext = %s\n", shared_libext);
fprintf (stderr, "library names:\n");
for (i = 0; i < libraries_no; i++)
{
fprintf (stderr, " %s\n", all_libraries[i]);
}
fprintf (stderr, "library paths:\n");
for (i = 0; i < paths_no; i++)
{
fprintf (stderr, " %s\n", library_paths[i]);
}
fprintf (stderr, "other flags:\n");
for (i = 0; i < other_flags_no; i++)
{
fprintf (stderr, " %s\n", other_flags[i]);
}
}
/* Output -L flags, and normalize them for our own use. */
{
int i;
normalized_library_paths = malloc (sizeof (char *) * paths_no);
for (i = 0; i < paths_no; i++)
{
printf (" -L%s", library_paths[i]);
#ifdef __MINGW32__
if (library_paths[i][0] == '/' && library_paths[i][1] == '/')
{
/* Convert //server/ to a format Windows functions understand */
char *s;
normalized_library_paths[i] = malloc ((PATH_MAX + 1) *
sizeof (char));
strcpy (normalized_library_paths[i], &(library_paths[i][2]));
s = strchr (path_rep, '/');
if (s)
{
*s = ':';
strcpy ((s + 1), &(library_paths[i][(int)(s - path_rep + 2)]));
}
}
else
#endif
normalized_library_paths[i] = library_paths[i];
}
}
/* Now output the libraries. */
{
int i;
for (i = 0; i < libraries_no; i++)
{
/* Search for the library, and print (using -l%s) the library
name to standard output. */
output_library_name (all_libraries[i], normalized_library_paths,
paths_no, debug, profile, shared);
}
}
/* Output the other flags */
{
int i;
for (i = 0; i < other_flags_no; i++)
{
printf (" %s", other_flags[i]);
}
}
puts ("");
return 0;
}