1 /* Interface to `ar' archives for GNU Make.
2 Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
3 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
4 2010 Free Software Foundation, Inc.
5 
6 This file is part of GNU Make.
7 
8 GNU Make is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 3 of the License, or (at your option) any later
11 version.
12 
13 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License along with
18 this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "make.h"
21 
22 #ifndef	NO_ARCHIVES
23 
24 #include "filedef.h"
25 #include "dep.h"
26 #include <fnmatch.h>
27 
28 /* Return nonzero if NAME is an archive-member reference, zero if not.  An
29    archive-member reference is a name like `lib(member)' where member is a
30    non-empty string.
31    If a name like `lib((entry))' is used, a fatal error is signaled at
32    the attempt to use this unsupported feature.  */
33 
34 int
ar_name(const char * name)35 ar_name (const char *name)
36 {
37   const char *p = strchr (name, '(');
38   const char *end;
39 
40   if (p == 0 || p == name)
41     return 0;
42 
43   end = p + strlen (p) - 1;
44   if (*end != ')' || end == p + 1)
45     return 0;
46 
47   if (p[1] == '(' && end[-1] == ')')
48     fatal (NILF, _("attempt to use unsupported feature: `%s'"), name);
49 
50   return 1;
51 }
52 
53 
54 /* Parse the archive-member reference NAME into the archive and member names.
55    Creates one allocated string containing both names, pointed to by ARNAME_P.
56    MEMNAME_P points to the member.  */
57 
58 void
ar_parse_name(const char * name,char ** arname_p,char ** memname_p)59 ar_parse_name (const char *name, char **arname_p, char **memname_p)
60 {
61   char *p;
62 
63   *arname_p = xstrdup (name);
64   p = strchr (*arname_p, '(');
65   *(p++) = '\0';
66   p[strlen(p) - 1] = '\0';
67   *memname_p = p;
68 }
69 
70 
71 /* This function is called by `ar_scan' to find which member to look at.  */
72 
73 /* ARGSUSED */
74 static long int
ar_member_date_1(int desc UNUSED,const char * mem,int truncated,long int hdrpos UNUSED,long int datapos UNUSED,long int size UNUSED,long int date,int uid UNUSED,int gid UNUSED,int mode UNUSED,const void * name)75 ar_member_date_1 (int desc UNUSED, const char *mem, int truncated,
76 		  long int hdrpos UNUSED, long int datapos UNUSED,
77                   long int size UNUSED, long int date,
78                   int uid UNUSED, int gid UNUSED, int mode UNUSED,
79 		  const void *name)
80 {
81   return ar_name_equal (name, mem, truncated) ? date : 0;
82 }
83 
84 /* Return the modtime of NAME.  */
85 
86 time_t
ar_member_date(const char * name)87 ar_member_date (const char *name)
88 {
89   char *arname;
90   char *memname;
91   long int val;
92 
93   ar_parse_name (name, &arname, &memname);
94 
95   /* Make sure we know the modtime of the archive itself because we are
96      likely to be called just before commands to remake a member are run,
97      and they will change the archive itself.
98 
99      But we must be careful not to enter_file the archive itself if it does
100      not exist, because pattern_search assumes that files found in the data
101      base exist or can be made.  */
102   {
103     struct file *arfile;
104     arfile = lookup_file (arname);
105     if (arfile == 0 && file_exists_p (arname))
106       arfile = enter_file (strcache_add (arname));
107 
108     if (arfile != 0)
109       (void) f_mtime (arfile, 0);
110   }
111 
112   val = ar_scan (arname, ar_member_date_1, memname);
113 
114   free (arname);
115 
116   return (val <= 0 ? (time_t) -1 : (time_t) val);
117 }
118 
119 /* Set the archive-member NAME's modtime to now.  */
120 
121 #ifdef VMS
122 int
ar_touch(const char * name)123 ar_touch (const char *name)
124 {
125   error (NILF, _("touch archive member is not available on VMS"));
126   return -1;
127 }
128 #else
129 int
ar_touch(const char * name)130 ar_touch (const char *name)
131 {
132   char *arname, *memname;
133   int val;
134 
135   ar_parse_name (name, &arname, &memname);
136 
137   /* Make sure we know the modtime of the archive itself before we
138      touch the member, since this will change the archive modtime.  */
139   {
140     struct file *arfile;
141     arfile = enter_file (strcache_add (arname));
142     f_mtime (arfile, 0);
143   }
144 
145   val = 1;
146   switch (ar_member_touch (arname, memname))
147     {
148     case -1:
149       error (NILF, _("touch: Archive `%s' does not exist"), arname);
150       break;
151     case -2:
152       error (NILF, _("touch: `%s' is not a valid archive"), arname);
153       break;
154     case -3:
155       perror_with_name ("touch: ", arname);
156       break;
157     case 1:
158       error (NILF,
159              _("touch: Member `%s' does not exist in `%s'"), memname, arname);
160       break;
161     case 0:
162       val = 0;
163       break;
164     default:
165       error (NILF,
166              _("touch: Bad return code from ar_member_touch on `%s'"), name);
167     }
168 
169   free (arname);
170 
171   return val;
172 }
173 #endif /* !VMS */
174 
175 /* State of an `ar_glob' run, passed to `ar_glob_match'.  */
176 
177 struct ar_glob_state
178   {
179     const char *arname;
180     const char *pattern;
181     unsigned int size;
182     struct nameseq *chain;
183     unsigned int n;
184   };
185 
186 /* This function is called by `ar_scan' to match one archive
187    element against the pattern in STATE.  */
188 
189 static long int
ar_glob_match(int desc UNUSED,const char * mem,int truncated UNUSED,long int hdrpos UNUSED,long int datapos UNUSED,long int size UNUSED,long int date UNUSED,int uid UNUSED,int gid UNUSED,int mode UNUSED,const void * arg)190 ar_glob_match (int desc UNUSED, const char *mem, int truncated UNUSED,
191 	       long int hdrpos UNUSED, long int datapos UNUSED,
192                long int size UNUSED, long int date UNUSED, int uid UNUSED,
193                int gid UNUSED, int mode UNUSED, const void *arg)
194 {
195   struct ar_glob_state *state = (struct ar_glob_state *)arg;
196 
197   if (fnmatch (state->pattern, mem, FNM_PATHNAME|FNM_PERIOD) == 0)
198     {
199       /* We have a match.  Add it to the chain.  */
200       struct nameseq *new = xcalloc (state->size);
201       new->name = strcache_add (concat (4, state->arname, "(", mem, ")"));
202       new->next = state->chain;
203       state->chain = new;
204       ++state->n;
205     }
206 
207   return 0L;
208 }
209 
210 /* Return nonzero if PATTERN contains any metacharacters.
211    Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
212 static int
glob_pattern_p(const char * pattern,int quote)213 glob_pattern_p (const char *pattern, int quote)
214 {
215   const char *p;
216   int opened = 0;
217 
218   for (p = pattern; *p != '\0'; ++p)
219     switch (*p)
220       {
221       case '?':
222       case '*':
223 	return 1;
224 
225       case '\\':
226 	if (quote)
227 	  ++p;
228 	break;
229 
230       case '[':
231 	opened = 1;
232 	break;
233 
234       case ']':
235 	if (opened)
236 	  return 1;
237 	break;
238       }
239 
240   return 0;
241 }
242 
243 /* Glob for MEMBER_PATTERN in archive ARNAME.
244    Return a malloc'd chain of matching elements (or nil if none).  */
245 
246 struct nameseq *
ar_glob(const char * arname,const char * member_pattern,unsigned int size)247 ar_glob (const char *arname, const char *member_pattern, unsigned int size)
248 {
249   struct ar_glob_state state;
250   struct nameseq *n;
251   const char **names;
252   unsigned int i;
253 
254   if (! glob_pattern_p (member_pattern, 1))
255     return 0;
256 
257   /* Scan the archive for matches.
258      ar_glob_match will accumulate them in STATE.chain.  */
259   state.arname = arname;
260   state.pattern = member_pattern;
261   state.size = size;
262   state.chain = 0;
263   state.n = 0;
264   ar_scan (arname, ar_glob_match, &state);
265 
266   if (state.chain == 0)
267     return 0;
268 
269   /* Now put the names into a vector for sorting.  */
270   names = alloca (state.n * sizeof (const char *));
271   i = 0;
272   for (n = state.chain; n != 0; n = n->next)
273     names[i++] = n->name;
274 
275   /* Sort them alphabetically.  */
276   /* MSVC erroneously warns without a cast here.  */
277   qsort ((void *)names, i, sizeof (*names), alpha_compare);
278 
279   /* Put them back into the chain in the sorted order.  */
280   i = 0;
281   for (n = state.chain; n != 0; n = n->next)
282     n->name = names[i++];
283 
284   return state.chain;
285 }
286 
287 #endif	/* Not NO_ARCHIVES.  */
288