3 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 * Copyright (c) 2008 Kai Wang
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/queue.h>
38 #include <archive_entry.h>
50 #define TEMPLATE "arscp.XXXXXXXX"
58 extern int yylex(void);
60 static void yyerror(const char *);
61 static void arscp_addlib(char *archive, struct list *list);
62 static void arscp_addmod(struct list *list);
63 static void arscp_clear(void);
64 static int arscp_copy(int ifd, int ofd);
65 static void arscp_create(char *in, char *out);
66 static void arscp_delete(struct list *list);
67 static void arscp_dir(char *archive, struct list *list, char *rlt);
68 static void arscp_end(int eval);
69 static void arscp_extract(struct list *list);
70 static void arscp_free_argv(void);
71 static void arscp_free_mlist(struct list *list);
72 static void arscp_list(void);
73 static struct list *arscp_mlist(struct list *list, char *str);
74 static void arscp_mlist2argv(struct list *list);
75 static int arscp_mlist_len(struct list *list);
76 static void arscp_open(char *fname);
77 static void arscp_prompt(void);
78 static void arscp_replace(struct list *list);
79 static void arscp_save(void);
80 static int arscp_target_exist(void);
84 static struct bsdar *bsdar;
87 static int interactive;
110 %type <list> mod_list
120 : { arscp_prompt(); } ar_script
129 : FNAME { $$ = arscp_mlist(NULL, $1); }
130 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); }
144 : cmd EOL { arscp_prompt(); }
167 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); }
168 | ADDLIB FNAME { arscp_addlib($2, NULL); }
172 : ADDMOD mod_list { arscp_addmod($2); }
176 : CLEAR { arscp_clear(); }
180 : CREATE FNAME { arscp_create(NULL, $2); }
184 : DELETE mod_list { arscp_delete($2); }
188 : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); }
189 | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); }
190 | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); }
194 : END { arscp_end(EX_OK); }
198 : EXTRACT mod_list { arscp_extract($2); }
202 : LIST { arscp_list(); }
206 : OPEN FNAME { arscp_open($2); }
210 : REPLACE mod_list { arscp_replace($2); }
214 : SAVE { arscp_save(); }
218 : VERBOSE { verbose = !verbose; }
226 : FNAME { yyerror(NULL); }
233 yyerror(const char *s)
237 printf("Syntax error in archive script, line %d\n", lineno);
241 * arscp_open first open an archive and check its validity. If the archive
242 * format is valid, it calls arscp_create to create a temporary copy of
246 arscp_open(char *fname)
249 struct archive_entry *entry;
252 if ((a = archive_read_new()) == NULL)
253 bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed");
254 archive_read_support_format_ar(a);
255 AC(archive_read_open_filename(a, fname, DEF_BLKSZ));
256 if ((r = archive_read_next_header(a, &entry)))
257 bsdar_warnc(bsdar, archive_errno(a), "%s",
258 archive_error_string(a));
259 AC(archive_read_close(a));
260 AC(archive_read_free(a));
263 arscp_create(fname, fname);
267 * Create archive. in != NULL indicate it's a OPEN cmd, and resulting
268 * archive is based on modification of an existing one. If in == NULL,
269 * we are in CREATE cmd and a new empty archive will be created.
272 arscp_create(char *in, char *out)
277 /* Delete previously created temporary archive, if any. */
279 if (unlink(tmpac) < 0)
280 bsdar_errc(bsdar, EX_IOERR, errno, "unlink failed");
284 tmpac = strdup(TEMPLATE);
286 bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
287 if ((ofd = mkstemp(tmpac)) < 0)
288 bsdar_errc(bsdar, EX_IOERR, errno, "mkstemp failed");
292 * Command OPEN creates a temporary copy of the
295 if ((ifd = open(in, O_RDONLY)) < 0) {
296 bsdar_warnc(bsdar, errno, "open failed");
299 if (arscp_copy(ifd, ofd)) {
300 bsdar_warnc(bsdar, 0, "arscp_copy failed");
307 * Command CREATE creates an "empty" archive.
308 * (archive with only global header)
310 if ((a = archive_write_new()) == NULL)
311 bsdar_errc(bsdar, EX_SOFTWARE, 0,
312 "archive_write_new failed");
313 archive_write_set_format_ar_svr4(a);
314 AC(archive_write_open_fd(a, ofd));
315 AC(archive_write_close(a));
316 AC(archive_write_free(a));
319 /* Override previous target, if any. */
324 bsdar->filename = tmpac;
327 /* A file copying implementation using mmap. */
329 arscp_copy(int ifd, int ofd)
336 if (fstat(ifd, &sb) < 0) {
337 bsdar_warnc(bsdar, errno, "fstate failed");
340 if ((p = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd,
341 (off_t)0)) == MAP_FAILED) {
342 bsdar_warnc(bsdar, errno, "mmap failed");
345 for (buf = p, bytes = sb.st_size; bytes > 0; bytes -= w) {
346 w = write(ofd, buf, bytes);
348 bsdar_warnc(bsdar, errno, "write failed");
352 if (munmap(p, sb.st_size) < 0)
353 bsdar_errc(bsdar, EX_SOFTWARE, errno, "munmap failed");
361 * Add all modules of archive to current archive, if list != NULL,
362 * only those modules specified in 'list' will be added.
365 arscp_addlib(char *archive, struct list *list)
368 if (!arscp_target_exist())
370 arscp_mlist2argv(list);
371 bsdar->addlib = archive;
374 arscp_free_mlist(list);
377 /* Add modules into current archive. */
379 arscp_addmod(struct list *list)
382 if (!arscp_target_exist())
384 arscp_mlist2argv(list);
387 arscp_free_mlist(list);
390 /* Delete modules from current archive. */
392 arscp_delete(struct list *list)
395 if (!arscp_target_exist())
397 arscp_mlist2argv(list);
400 arscp_free_mlist(list);
403 /* Extract modules from current archive. */
405 arscp_extract(struct list *list)
408 if (!arscp_target_exist())
410 arscp_mlist2argv(list);
413 arscp_free_mlist(list);
416 /* List modules of archive. (Simple Mode) */
421 if (!arscp_target_exist())
425 /* Always verbose. */
426 bsdar->options |= AR_V;
428 bsdar->options &= ~AR_V;
431 /* List modules of archive. (Advance Mode) */
433 arscp_dir(char *archive, struct list *list, char *rlt)
437 /* If rlt != NULL, redirect output to it */
441 if ((stdout = fopen(rlt, "w")) == NULL)
442 bsdar_errc(bsdar, EX_IOERR, errno,
443 "fopen %s failed", rlt);
446 bsdar->filename = archive;
448 arscp_mlist2argv(list);
454 bsdar->options |= AR_V;
456 bsdar->options &= ~AR_V;
459 if (fclose(stdout) == EOF)
460 bsdar_errc(bsdar, EX_IOERR, errno,
461 "fclose %s failed", rlt);
466 bsdar->filename = tmpac;
468 arscp_free_mlist(list);
472 /* Replace modules of current archive. */
474 arscp_replace(struct list *list)
477 if (!arscp_target_exist())
479 arscp_mlist2argv(list);
482 arscp_free_mlist(list);
485 /* Rename the temporary archive to the target archive. */
492 if (rename(tmpac, target) < 0)
493 bsdar_errc(bsdar, EX_IOERR, errno, "rename failed");
495 * mkstemp creates temp files with mode 0600, here we
496 * set target archive mode per process umask.
500 if (chmod(target, 0666 & ~mask) < 0)
501 bsdar_errc(bsdar, EX_IOERR, errno, "chmod failed");
506 bsdar->filename = NULL;
508 bsdar_warnc(bsdar, 0, "no open output archive");
512 * Discard all the contents of current archive. This is achieved by
513 * invoking CREATE cmd on current archive.
521 new_target = strdup(target);
522 if (new_target == NULL)
523 bsdar_errc(bsdar, EX_SOFTWARE, errno, "strdup failed");
524 arscp_create(NULL, new_target);
529 * Quit ar(1). Note that END cmd will not SAVE current archive
539 if (unlink(tmpac) == -1)
540 bsdar_errc(bsdar, EX_IOERR, errno, "unlink %s failed",
549 * Check if target specified, i.e, whether OPEN or CREATE has been
553 arscp_target_exist(void)
559 bsdar_warnc(bsdar, 0, "no open output archive");
563 /* Construct module list. */
565 arscp_mlist(struct list *list, char *str)
569 l = malloc(sizeof(*l));
571 bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
578 /* Calculate the length of a mlist. */
580 arscp_mlist_len(struct list *list)
584 for(len = 0; list; list = list->next)
590 /* Free the space allocated for mod_list. */
592 arscp_free_mlist(struct list *list)
596 /* Note that list->str was freed in arscp_free_argv. */
597 for(; list; list = l) {
603 /* Convert mlist to argv array. */
605 arscp_mlist2argv(struct list *list)
610 n = arscp_mlist_len(list);
611 argv = malloc(n * sizeof(*argv));
613 bsdar_errc(bsdar, EX_SOFTWARE, errno, "malloc failed");
615 /* Note that module names are stored in reverse order in mlist. */
616 for(i = n - 1; i >= 0; i--, list = list->next) {
618 bsdar_errc(bsdar, EX_SOFTWARE, errno, "invalid mlist");
626 /* Free space allocated for argv array and its elements. */
628 arscp_free_argv(void)
632 for(i = 0; i < bsdar->argc; i++)
633 free(bsdar->argv[i]);
638 /* Show a prompt if we are in interactive mode */
649 /* Main function for ar script mode. */
651 ar_mode_script(struct bsdar *ar)
655 interactive = isatty(fileno(stdin));
661 /* Script ends without END */