3 * Copyright (c) 2008 Kai Wang
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID
("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/queue.h>
36 #include <archive_entry.h>
48 #define TEMPLATE "arscp.XXXXXXXX"
56 extern
int yylex(void);
58 static void yyerror(const char *);
59 static void arscp_addlib
(char *archive
, struct list
*list
);
60 static void arscp_addmod
(struct list
*list
);
61 static void arscp_clear
(void);
62 static int arscp_copy
(int ifd
, int ofd
);
63 static void arscp_create
(char *in
, char *out
);
64 static void arscp_delete
(struct list
*list
);
65 static void arscp_dir
(char *archive
, struct list
*list
, char *rlt
);
66 static void arscp_end
(int eval
);
67 static void arscp_extract
(struct list
*list
);
68 static void arscp_free_argv
(void);
69 static void arscp_free_mlist
(struct list
*list
);
70 static void arscp_list
(void);
71 static struct list
*arscp_mlist
(struct list
*list
, char *str
);
72 static void arscp_mlist2argv
(struct list
*list
);
73 static int arscp_mlist_len
(struct list
*list
);
74 static void arscp_open
(char *fname
);
75 static void arscp_prompt
(void);
76 static void arscp_replace
(struct list
*list
);
77 static void arscp_save
(void);
78 static int arscp_target_exist
(void);
82 static struct bsdar
*bsdar
;
85 static int interactive
;
108 %type
<list
> mod_list
118 : { arscp_prompt
(); } ar_script
127 : FNAME
{ $$
= arscp_mlist
(NULL
, $1); }
128 | mod_list separator FNAME
{ $$
= arscp_mlist
($1, $3); }
142 : cmd EOL
{ arscp_prompt
(); }
165 : ADDLIB FNAME LP mod_list RP
{ arscp_addlib
($2, $4); }
166 | ADDLIB FNAME
{ arscp_addlib
($2, NULL
); }
170 : ADDMOD mod_list
{ arscp_addmod
($2); }
174 : CLEAR
{ arscp_clear
(); }
178 : CREATE FNAME
{ arscp_create
(NULL
, $2); }
182 : DELETE mod_list
{ arscp_delete
($2); }
186 : DIRECTORY FNAME
{ arscp_dir
($2, NULL
, NULL
); }
187 | DIRECTORY FNAME LP mod_list RP
{ arscp_dir
($2, $4, NULL
); }
188 | DIRECTORY FNAME LP mod_list RP FNAME
{ arscp_dir
($2, $4, $6); }
192 : END
{ arscp_end
(EX_OK
); }
196 : EXTRACT mod_list
{ arscp_extract
($2); }
200 : LIST
{ arscp_list
(); }
204 : OPEN FNAME
{ arscp_open
($2); }
208 : REPLACE mod_list
{ arscp_replace
($2); }
212 : SAVE
{ arscp_save
(); }
216 : VERBOSE
{ verbose
= !verbose
; }
224 : FNAME
{ yyerror(NULL
); }
231 yyerror(const char *s
)
235 printf
("Syntax error in archive script, line %d\n", lineno
);
239 * arscp_open first open an archive and check its validity. If the archive
240 * format is valid, it calls arscp_create to create a temporary copy of
244 arscp_open
(char *fname
)
247 struct archive_entry
*entry
;
250 if
((a
= archive_read_new
()) == NULL
)
251 bsdar_errc
(bsdar
, EX_SOFTWARE
, 0, "archive_read_new failed");
252 archive_read_support_format_ar
(a
);
253 AC
(archive_read_open_filename
(a
, fname
, DEF_BLKSZ
));
254 if
((r
= archive_read_next_header
(a
, &entry
)))
255 bsdar_warnc
(bsdar
, 0, "%s", archive_error_string
(a
));
256 AC
(archive_read_close
(a
));
257 AC
(archive_read_free
(a
));
260 arscp_create
(fname
, fname
);
264 * Create archive. in != NULL indicate it's a OPEN cmd, and resulting
265 * archive is based on modification of an existing one. If in == NULL,
266 * we are in CREATE cmd and a new empty archive will be created.
269 arscp_create
(char *in
, char *out
)
274 /* Delete previously created temporary archive, if any. */
276 if
(unlink
(tmpac
) < 0)
277 bsdar_errc
(bsdar
, EX_IOERR
, errno
, "unlink failed");
281 tmpac
= strdup
(TEMPLATE
);
283 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "strdup failed");
284 if
((ofd
= mkstemp
(tmpac
)) < 0)
285 bsdar_errc
(bsdar
, EX_IOERR
, errno
, "mkstemp failed");
289 * Command OPEN creates a temporary copy of the
292 if
((ifd
= open
(in
, O_RDONLY
)) < 0) {
293 bsdar_warnc
(bsdar
, errno
, "open failed");
296 if
(arscp_copy
(ifd
, ofd
)) {
297 bsdar_warnc
(bsdar
, 0, "arscp_copy failed");
304 * Command CREATE creates an "empty" archive.
305 * (archive with only global header)
307 if
((a
= archive_write_new
()) == NULL
)
308 bsdar_errc
(bsdar
, EX_SOFTWARE
, 0,
309 "archive_write_new failed");
310 archive_write_set_format_ar_svr4
(a
);
311 AC
(archive_write_open_fd
(a
, ofd
));
312 AC
(archive_write_close
(a
));
313 AC
(archive_write_free
(a
));
316 /* Override previous target, if any. */
321 bsdar
->filename
= tmpac
;
324 /* A file copying implementation using mmap. */
326 arscp_copy
(int ifd
, int ofd
)
333 if
(fstat
(ifd
, &sb
) < 0) {
334 bsdar_warnc
(bsdar
, errno
, "fstate failed");
337 if
((p
= mmap
(NULL
, sb.st_size
, PROT_READ
, MAP_SHARED
, ifd
,
338 (off_t
)0)) == MAP_FAILED
) {
339 bsdar_warnc
(bsdar
, errno
, "mmap failed");
342 for
(buf
= p
, bytes
= sb.st_size
; bytes
> 0; bytes
-= w
) {
343 w
= write
(ofd
, buf
, bytes
);
345 bsdar_warnc
(bsdar
, errno
, "write failed");
349 if
(munmap
(p
, sb.st_size
) < 0)
350 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "munmap failed");
358 * Add all modules of archive to current archive, if list != NULL,
359 * only those modules specified in 'list' will be added.
362 arscp_addlib
(char *archive
, struct list
*list
)
365 if
(!arscp_target_exist
())
367 arscp_mlist2argv
(list
);
368 bsdar
->addlib
= archive
;
371 arscp_free_mlist
(list
);
374 /* Add modules into current archive. */
376 arscp_addmod
(struct list
*list
)
379 if
(!arscp_target_exist
())
381 arscp_mlist2argv
(list
);
384 arscp_free_mlist
(list
);
387 /* Delete modules from current archive. */
389 arscp_delete
(struct list
*list
)
392 if
(!arscp_target_exist
())
394 arscp_mlist2argv
(list
);
397 arscp_free_mlist
(list
);
400 /* Extract modules from current archive. */
402 arscp_extract
(struct list
*list
)
405 if
(!arscp_target_exist
())
407 arscp_mlist2argv
(list
);
410 arscp_free_mlist
(list
);
413 /* List modules of archive. (Simple Mode) */
418 if
(!arscp_target_exist
())
422 /* Always verbose. */
423 bsdar
->options |
= AR_V
;
425 bsdar
->options
&= ~AR_V
;
428 /* List modules of archive. (Advance Mode) */
430 arscp_dir
(char *archive
, struct list
*list
, char *rlt
)
434 /* If rlt != NULL, redirect output to it */
438 if
((stdout
= fopen
(rlt
, "w")) == NULL
)
439 bsdar_errc
(bsdar
, EX_IOERR
, errno
,
440 "fopen %s failed", rlt
);
443 bsdar
->filename
= archive
;
445 arscp_mlist2argv
(list
);
451 bsdar
->options |
= AR_V
;
453 bsdar
->options
&= ~AR_V
;
456 if
(fclose
(stdout
) == EOF
)
457 bsdar_errc
(bsdar
, EX_IOERR
, errno
,
458 "fclose %s failed", rlt
);
463 bsdar
->filename
= tmpac
;
465 arscp_free_mlist
(list
);
469 /* Replace modules of current archive. */
471 arscp_replace
(struct list
*list
)
474 if
(!arscp_target_exist
())
476 arscp_mlist2argv
(list
);
479 arscp_free_mlist
(list
);
482 /* Rename the temporary archive to the target archive. */
489 if
(rename
(tmpac
, target
) < 0)
490 bsdar_errc
(bsdar
, EX_IOERR
, errno
, "rename failed");
492 * mkstemp creates temp files with mode 0600, here we
493 * set target archive mode per process umask.
497 if
(chmod
(target
, 0666 & ~mask
) < 0)
498 bsdar_errc
(bsdar
, EX_IOERR
, errno
, "chmod failed");
503 bsdar
->filename
= NULL
;
505 bsdar_warnc
(bsdar
, 0, "no open output archive");
509 * Discard all the contents of current archive. This is achieved by
510 * invoking CREATE cmd on current archive.
518 new_target
= strdup
(target
);
519 if
(new_target
== NULL
)
520 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "strdup failed");
521 arscp_create
(NULL
, new_target
);
526 * Quit ar(1). Note that END cmd will not SAVE current archive
536 if
(unlink
(tmpac
) == -1)
537 bsdar_errc
(bsdar
, EX_IOERR
, errno
, "unlink %s failed",
546 * Check if target specified, i.e, whether OPEN or CREATE has been
550 arscp_target_exist
(void)
556 bsdar_warnc
(bsdar
, 0, "no open output archive");
560 /* Construct module list. */
562 arscp_mlist
(struct list
*list
, char *str
)
566 l
= malloc
(sizeof
(*l
));
568 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "malloc failed");
575 /* Calculate the length of a mlist. */
577 arscp_mlist_len
(struct list
*list
)
581 for
(len
= 0; list
; list
= list
->next
)
587 /* Free the space allocated for mod_list. */
589 arscp_free_mlist
(struct list
*list
)
593 /* Note that list->str was freed in arscp_free_argv. */
594 for
(; list
; list
= l
) {
600 /* Convert mlist to argv array. */
602 arscp_mlist2argv
(struct list
*list
)
607 n
= arscp_mlist_len
(list
);
608 argv
= malloc
(n
* sizeof
(*argv
));
610 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "malloc failed");
612 /* Note that module names are stored in reverse order in mlist. */
613 for
(i
= n
- 1; i
>= 0; i
--, list
= list
->next
) {
615 bsdar_errc
(bsdar
, EX_SOFTWARE
, errno
, "invalid mlist");
623 /* Free space allocated for argv array and its elements. */
625 arscp_free_argv
(void)
629 for
(i
= 0; i
< bsdar
->argc
; i
++)
630 free
(bsdar
->argv
[i
]);
635 /* Show a prompt if we are in interactive mode */
646 /* Main function for ar script mode. */
648 ar_mode_script
(struct bsdar
*ar
)
652 interactive
= isatty
(fileno
(stdin
));
658 /* Script ends without END */