1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1988 AT&T */
22 /* All Rights Reserved */
23
24 /*
25 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
26 */
27
28 /*
29 * Copyright (c) 2018, Joyent, Inc.
30 * Copyright 2022 Oxide Computer Company
31 */
32
33 #include "inc.h"
34 #include "conv.h"
35
36 /*
37 * Forward declarations
38 */
39 static void setup(int, char **, Cmd_info *);
40 static void setcom(Cmd_info *, Cmd_func);
41 static void usage(void);
42 static void sigexit(int sig);
43 static int notfound(Cmd_info *);
44 static void check_swap();
45
46 const char *
_ar_msg(Msg mid)47 _ar_msg(Msg mid)
48 {
49 return (gettext(MSG_ORIG(mid)));
50 }
51
52
53 void
establish_sighandler(void (* handler)())54 establish_sighandler(void (*handler)())
55 {
56 static const int signum[] = {SIGHUP, SIGINT, SIGQUIT, 0};
57 int i;
58
59 if (handler == SIG_IGN) {
60 /* Ignore all the specified signals */
61 for (i = 0; signum[i]; i++)
62 (void) signal(signum[i], SIG_IGN);
63
64 } else {
65 /*
66 * Set any signal that doesn't default to being ignored
67 * to our signal handler.
68 */
69 for (i = 0; signum[i]; i++)
70 if (signal(signum[i], SIG_IGN) != SIG_IGN)
71 (void) signal(signum[i], handler);
72 }
73 }
74
75 int
main(int argc,char ** argv,char * envp[])76 main(int argc, char **argv, char *envp[])
77 {
78 int fd;
79 Cmd_info *cmd_info;
80 int ret;
81 char *new = NULL;
82
83 #ifndef XPG4
84 /*
85 * Check for a binary that better fits this architecture.
86 */
87 (void) conv_check_native(argv, envp);
88 #endif
89
90 /*
91 * Establish locale.
92 */
93 (void) setlocale(LC_ALL, MSG_ORIG(MSG_STR_EMPTY));
94 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
95
96 /* Allow a graceful exit up until we start to write an archive */
97 establish_sighandler(sigexit);
98
99 /*
100 * Initialize cmd_info
101 */
102 cmd_info = (Cmd_info *)calloc(1, sizeof (Cmd_info));
103 if (cmd_info == NULL) {
104 int err = errno;
105 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC), strerror(err));
106 exit(1);
107 }
108
109 if (argc < 2)
110 usage();
111
112 /*
113 * Option handling.
114 */
115 if (argv[1][0] != '-') {
116 new = (char *)malloc(strlen(argv[1]) + 2);
117 if (new == NULL) {
118 int err = errno;
119 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
120 strerror(err));
121 exit(1);
122 }
123 (void) strcpy(new, MSG_ORIG(MSG_STR_HYPHEN));
124 (void) strcat(new, argv[1]);
125 argv[1] = new;
126 }
127 setup(argc, argv, cmd_info);
128
129 /*
130 * Check SWAP
131 */
132 if (cmd_info->opt_flgs & z_FLAG)
133 check_swap();
134
135 cmd_info->modified = (cmd_info->opt_flgs & s_FLAG);
136 fd = getaf(cmd_info);
137
138 if (fd == -1) {
139 boolean_t req_arg = (cmd_info->opt_flgs & (d_FLAG | m_FLAG |
140 p_FLAG | t_FLAG | x_FLAG)) != 0;
141 boolean_t req_r = (cmd_info->opt_flgs & r_FLAG) &&
142 (cmd_info->opt_flgs & (a_FLAG | b_FLAG));
143 boolean_t req_s = (cmd_info->opt_flgs & s_FLAG) &&
144 (cmd_info->opt_flgs & (r_FLAG | q_FLAG)) == 0;
145
146 if (req_arg || req_r || req_s) {
147 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_AR),
148 cmd_info->arnam);
149 exit(1);
150 }
151 }
152
153 (*cmd_info->comfun)(cmd_info);
154 if (cmd_info->modified) {
155 writefile(cmd_info);
156 } else
157 (void) close(fd);
158
159 ret = notfound(cmd_info);
160
161 /*
162 * Check SWAP
163 */
164 if (cmd_info->opt_flgs & z_FLAG)
165 check_swap();
166
167 free(new);
168 free(cmd_info);
169 return (ret);
170
171 }
172
173 /*
174 * Option handing function.
175 * Using getopt(), following xcu4 convention.
176 */
177 static void
setup(int argc,char * argv[],Cmd_info * cmd_info)178 setup(int argc, char *argv[], Cmd_info *cmd_info)
179 {
180 int Vflag = 0;
181 int c;
182 int usage_err = 0;
183
184 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) {
185 switch (c) {
186 case 'a': /* position after named archive member file */
187 cmd_info->opt_flgs |= a_FLAG;
188 cmd_info->ponam = trim(optarg);
189 break;
190 case 'b': /* position before named archive member file */
191 case 'i': /* position before named archive member: same as b */
192 cmd_info->opt_flgs |= b_FLAG;
193 cmd_info->ponam = trim(optarg);
194 break;
195 case 'c': /* supress messages */
196 cmd_info->opt_flgs |= c_FLAG;
197 break;
198 case 'd':
199 /*
200 * key operation:
201 * delete files from the archive
202 */
203 setcom(cmd_info, dcmd);
204 cmd_info->opt_flgs |= d_FLAG;
205 break;
206 case 'l': /* ignored */
207 break;
208 case 'm':
209 /*
210 * key operation:
211 * move files to end of the archive
212 * or as indicated by position flag
213 */
214 setcom(cmd_info, mcmd);
215 cmd_info->opt_flgs |= m_FLAG;
216 break;
217 case 'p':
218 /*
219 * key operation:
220 * print files in the archive
221 */
222 setcom(cmd_info, pcmd);
223 cmd_info->opt_flgs |= p_FLAG;
224 break;
225 case 'q':
226 /*
227 * key operation:
228 * quickly append files to end of the archive
229 */
230 setcom(cmd_info, qcmd);
231 cmd_info->opt_flgs |= q_FLAG;
232 break;
233 case 'r':
234 /*
235 * key operation:
236 * replace or add files to the archive
237 */
238 setcom(cmd_info, rcmd);
239 cmd_info->opt_flgs |= r_FLAG;
240 break;
241 case 's': /* force symbol table regeneration */
242 cmd_info->opt_flgs |= s_FLAG;
243 break;
244 case 'S': /* Build SYM64 symbol table */
245 cmd_info->opt_flgs |= S_FLAG;
246 break;
247 case 't':
248 /*
249 * key operation:
250 * print table of contents
251 */
252 setcom(cmd_info, tcmd);
253 cmd_info->opt_flgs |= t_FLAG;
254 break;
255 case 'u': /* update: change archive dependent on file dates */
256 cmd_info->opt_flgs |= u_FLAG;
257 break;
258 case 'v': /* verbose */
259 cmd_info->opt_flgs |= v_FLAG;
260 break;
261 case 'x':
262 /*
263 * key operation:
264 * extract files from the archive
265 */
266 setcom(cmd_info, xcmd);
267 cmd_info->opt_flgs |= x_FLAG;
268 break;
269 case 'z':
270 cmd_info->opt_flgs |= z_FLAG;
271 break;
272 case 'V':
273 /*
274 * print version information.
275 * adjust command line access accounting
276 */
277 if (Vflag == 0) {
278 (void) fprintf(stderr,
279 MSG_ORIG(MSG_FMT_VERSION),
280 (const char *)SGU_PKG,
281 (const char *)SGU_REL);
282 Vflag++;
283 }
284 break;
285 case 'C':
286 cmd_info->opt_flgs |= C_FLAG;
287 break;
288 case 'M':
289 /*
290 * -M was an original undocumented AT&T feature that
291 * would force the use of mmap() instead of read()
292 * for pulling file data into the process before
293 * writing it to the archive. Ignored.
294 */
295 break;
296 case 'T':
297 cmd_info->opt_flgs |= T_FLAG;
298 break;
299 case ':':
300 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPERAND),
301 optopt);
302 usage_err++;
303 break;
304 case '?':
305 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPTION),
306 optopt);
307 usage_err++;
308 break;
309 }
310 }
311
312 if (usage_err || argc - optind < 1)
313 usage();
314
315 cmd_info->arnam = argv[optind];
316 cmd_info->namv = &argv[optind+1];
317 cmd_info->namc = argc - optind - 1;
318
319 /*
320 * GNU ar popularized the use of -s on its own which previously used to
321 * require another command function. As such, we don't set a command
322 * function when we encounter the -s flag because that might otherwise
323 * clobber an existing one being set and would interrupt the detection
324 * of multiple flags being used that way.
325 *
326 * If after processing everything, we find there's no command function
327 * set and the -s flag has been set, then we can finally set a command
328 * function. The command function for -t 'tcmd' is used in this case. It
329 * knows to only print out data if -t has been specified.
330 *
331 * While ar has not traditionally been very stringent about using flags
332 * in circumstances they aren't called for, we go ahead and check for
333 * that now for this newer option.
334 */
335 if (cmd_info->comfun == NULL) {
336 if ((cmd_info->opt_flgs & s_FLAG) != 0) {
337 if ((cmd_info->opt_flgs & ~(s_FLAG | v_FLAG)) != 0) {
338 (void) fprintf(stderr,
339 MSG_INTL(MSG_USAGE_S_BAD_ARG));
340 exit(1);
341 }
342
343 if (cmd_info->namc > 0) {
344 (void) fprintf(stderr,
345 MSG_INTL(MSG_USAGE_S_EXTRA_AR));
346 exit(1);
347 }
348
349 setcom(cmd_info, tcmd);
350 } else if ((cmd_info->opt_flgs & (d_FLAG | r_FLAG | q_FLAG |
351 s_FLAG | t_FLAG | p_FLAG | m_FLAG | x_FLAG)) == 0) {
352 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_REQ_FLAG));
353 exit(1);
354 }
355 }
356 }
357
358
359 /*
360 * Set the function to be called to do the key operation.
361 * Check that only one key is indicated.
362 */
363 static void
setcom(Cmd_info * cmd_info,Cmd_func * fun)364 setcom(Cmd_info *cmd_info, Cmd_func *fun)
365 {
366 if (cmd_info->comfun != NULL) {
367 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_TOO_MANY));
368 exit(1);
369 }
370 cmd_info->comfun = fun;
371 }
372
373 static void
usage(void)374 usage(void)
375 {
376 (void) fprintf(stderr, MSG_INTL(MSG_USAGE));
377 exit(1);
378 }
379
380 /*ARGSUSED0*/
381 static void
sigexit(int sig)382 sigexit(int sig)
383 {
384 exit(100);
385 }
386
387 /* tells the user which of the listed files were not found in the archive */
388
389 static int
notfound(Cmd_info * cmd_info)390 notfound(Cmd_info *cmd_info)
391 {
392 int i, n;
393
394 n = 0;
395 for (i = 0; i < cmd_info->namc; i++)
396 if (cmd_info->namv[i]) {
397 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_FILE),
398 cmd_info->namv[i]);
399 n++;
400 }
401 return (n);
402 }
403
404 /*
405 * Debugging info
406 */
407 static void
check_swap(void)408 check_swap(void)
409 {
410 (void) system(MSG_ORIG(MSG_CMD_SWAP));
411 }
412