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