1 %{
2 /*-
3 * SPDX-License-Identifier: BSD-2-Clause
4 *
5 * Copyright (c) 2008 Kai Wang
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 #include <sys/mman.h>
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <sys/stat.h>
35 #include <archive.h>
36 #include <archive_entry.h>
37 #include <dirent.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "ar.h"
46
47 #define TEMPLATE "arscp.XXXXXXXX"
48
49 struct list {
50 char *str;
51 struct list *next;
52 };
53
54
55 extern int yylex(void);
56
57 static void yyerror(const char *);
58 static void arscp_addlib(char *archive, struct list *list);
59 static void arscp_addmod(struct list *list);
60 static void arscp_clear(void);
61 static int arscp_copy(int ifd, int ofd);
62 static void arscp_create(char *in, char *out);
63 static void arscp_delete(struct list *list);
64 static void arscp_dir(char *archive, struct list *list, char *rlt);
65 static void arscp_end(int eval);
66 static void arscp_extract(struct list *list);
67 static void arscp_free_argv(void);
68 static void arscp_free_mlist(struct list *list);
69 static void arscp_list(void);
70 static struct list *arscp_mlist(struct list *list, char *str);
71 static void arscp_mlist2argv(struct list *list);
72 static int arscp_mlist_len(struct list *list);
73 static void arscp_open(char *fname);
74 static void arscp_prompt(void);
75 static void arscp_replace(struct list *list);
76 static void arscp_save(void);
77 static int arscp_target_exist(void);
78
79 extern int lineno;
80
81 static struct bsdar *bsdar;
82 static char *target;
83 static char *tmpac;
84 static int interactive;
85 static int verbose;
86
87 %}
88
89 %token ADDLIB
90 %token ADDMOD
91 %token CLEAR
92 %token CREATE
93 %token DELETE
94 %token DIRECTORY
95 %token END
96 %token EXTRACT
97 %token LIST
98 %token OPEN
99 %token REPLACE
100 %token VERBOSE
101 %token SAVE
102 %token LP
103 %token RP
104 %token COMMA
105 %token EOL
106 %token <str> FNAME
107 %type <list> mod_list
108
109 %union {
110 char *str;
111 struct list *list;
112 }
113
114 %%
115
116 begin
117 : { arscp_prompt(); } ar_script
118 ;
119
120 ar_script
121 : cmd_list
122 |
123 ;
124
125 mod_list
126 : FNAME { $$ = arscp_mlist(NULL, $1); }
127 | mod_list separator FNAME { $$ = arscp_mlist($1, $3); }
128 ;
129
130 separator
131 : COMMA
132 |
133 ;
134
135 cmd_list
136 : rawcmd
137 | cmd_list rawcmd
138 ;
139
140 rawcmd
141 : cmd EOL { arscp_prompt(); }
142 ;
143
144 cmd
145 : addlib_cmd
146 | addmod_cmd
147 | clear_cmd
148 | create_cmd
149 | delete_cmd
150 | directory_cmd
151 | end_cmd
152 | extract_cmd
153 | list_cmd
154 | open_cmd
155 | replace_cmd
156 | verbose_cmd
157 | save_cmd
158 | invalid_cmd
159 | empty_cmd
160 | error
161 ;
162
163 addlib_cmd
164 : ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); }
165 | ADDLIB FNAME { arscp_addlib($2, NULL); }
166 ;
167
168 addmod_cmd
169 : ADDMOD mod_list { arscp_addmod($2); }
170 ;
171
172 clear_cmd
173 : CLEAR { arscp_clear(); }
174 ;
175
176 create_cmd
177 : CREATE FNAME { arscp_create(NULL, $2); }
178 ;
179
180 delete_cmd
181 : DELETE mod_list { arscp_delete($2); }
182 ;
183
184 directory_cmd
185 : DIRECTORY FNAME { arscp_dir($2, NULL, NULL); }
186 | DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); }
187 | DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); }
188 ;
189
190 end_cmd
191 : END { arscp_end(EXIT_SUCCESS); }
192 ;
193
194 extract_cmd
195 : EXTRACT mod_list { arscp_extract($2); }
196 ;
197
198 list_cmd
199 : LIST { arscp_list(); }
200 ;
201
202 open_cmd
203 : OPEN FNAME { arscp_open($2); }
204 ;
205
206 replace_cmd
207 : REPLACE mod_list { arscp_replace($2); }
208 ;
209
210 save_cmd
211 : SAVE { arscp_save(); }
212 ;
213
214 verbose_cmd
215 : VERBOSE { verbose = !verbose; }
216 ;
217
218 empty_cmd
219 :
220 ;
221
222 invalid_cmd
223 : FNAME { yyerror(NULL); }
224 ;
225
226 %%
227
228 /* ARGSUSED */
229 static void
230 yyerror(const char *s)
231 {
232
233 (void) s;
234 printf("Syntax error in archive script, line %d\n", lineno);
235 }
236
237 /*
238 * arscp_open first open an archive and check its validity. If the archive
239 * format is valid, it calls arscp_create to create a temporary copy of
240 * the archive.
241 */
242 static void
arscp_open(char * fname)243 arscp_open(char *fname)
244 {
245 struct archive *a;
246 struct archive_entry *entry;
247 int r;
248
249 if ((a = archive_read_new()) == NULL)
250 bsdar_errc(bsdar, 0, "archive_read_new failed");
251 archive_read_support_format_ar(a);
252 AC(archive_read_open_filename(a, fname, DEF_BLKSZ));
253 if ((r = archive_read_next_header(a, &entry)))
254 bsdar_warnc(bsdar, archive_errno(a), "%s",
255 archive_error_string(a));
256 AC(archive_read_close(a));
257 AC(archive_read_free(a));
258 if (r != ARCHIVE_OK)
259 return;
260 arscp_create(fname, fname);
261 }
262
263 /*
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.
267 */
268 static void
arscp_create(char * in,char * out)269 arscp_create(char *in, char *out)
270 {
271 struct archive *a;
272 int ifd, ofd;
273
274 /* Delete previously created temporary archive, if any. */
275 if (tmpac) {
276 if (unlink(tmpac) < 0)
277 bsdar_errc(bsdar, errno, "unlink failed");
278 free(tmpac);
279 }
280
281 tmpac = strdup(TEMPLATE);
282 if (tmpac == NULL)
283 bsdar_errc(bsdar, errno, "strdup failed");
284 if ((ofd = mkstemp(tmpac)) < 0)
285 bsdar_errc(bsdar, errno, "mkstemp failed");
286
287 if (in) {
288 /*
289 * Command OPEN creates a temporary copy of the
290 * input archive.
291 */
292 if ((ifd = open(in, O_RDONLY)) < 0) {
293 bsdar_warnc(bsdar, errno, "open failed");
294 return;
295 }
296 if (arscp_copy(ifd, ofd)) {
297 bsdar_warnc(bsdar, 0, "arscp_copy failed");
298 return;
299 }
300 close(ifd);
301 close(ofd);
302 } else {
303 /*
304 * Command CREATE creates an "empty" archive.
305 * (archive with only global header)
306 */
307 if ((a = archive_write_new()) == NULL)
308 bsdar_errc(bsdar, 0, "archive_write_new failed");
309 archive_write_set_format_ar_svr4(a);
310 AC(archive_write_open_fd(a, ofd));
311 AC(archive_write_close(a));
312 AC(archive_write_free(a));
313 }
314
315 /* Override previous target, if any. */
316 if (target)
317 free(target);
318
319 target = out;
320 bsdar->filename = tmpac;
321 }
322
323 /* A file copying implementation using mmap. */
324 static int
arscp_copy(int ifd,int ofd)325 arscp_copy(int ifd, int ofd)
326 {
327 struct stat sb;
328 char *buf, *p;
329 ssize_t w;
330 size_t bytes;
331
332 if (fstat(ifd, &sb) < 0) {
333 bsdar_warnc(bsdar, errno, "fstate failed");
334 return (1);
335 }
336 if ((p = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd,
337 (off_t)0)) == MAP_FAILED) {
338 bsdar_warnc(bsdar, errno, "mmap failed");
339 return (1);
340 }
341 for (buf = p, bytes = sb.st_size; bytes > 0; bytes -= w) {
342 w = write(ofd, buf, bytes);
343 if (w <= 0) {
344 bsdar_warnc(bsdar, errno, "write failed");
345 break;
346 }
347 }
348 if (munmap(p, sb.st_size) < 0)
349 bsdar_errc(bsdar, errno, "munmap failed");
350 if (bytes > 0)
351 return (1);
352
353 return (0);
354 }
355
356 /*
357 * Add all modules of archive to current archive, if list != NULL,
358 * only those modules specified in 'list' will be added.
359 */
360 static void
arscp_addlib(char * archive,struct list * list)361 arscp_addlib(char *archive, struct list *list)
362 {
363
364 if (!arscp_target_exist())
365 return;
366 arscp_mlist2argv(list);
367 bsdar->addlib = archive;
368 ar_write_archive(bsdar, 'A');
369 arscp_free_argv();
370 arscp_free_mlist(list);
371 }
372
373 /* Add modules into current archive. */
374 static void
arscp_addmod(struct list * list)375 arscp_addmod(struct list *list)
376 {
377
378 if (!arscp_target_exist())
379 return;
380 arscp_mlist2argv(list);
381 ar_write_archive(bsdar, 'q');
382 arscp_free_argv();
383 arscp_free_mlist(list);
384 }
385
386 /* Delete modules from current archive. */
387 static void
arscp_delete(struct list * list)388 arscp_delete(struct list *list)
389 {
390
391 if (!arscp_target_exist())
392 return;
393 arscp_mlist2argv(list);
394 ar_write_archive(bsdar, 'd');
395 arscp_free_argv();
396 arscp_free_mlist(list);
397 }
398
399 /* Extract modules from current archive. */
400 static void
arscp_extract(struct list * list)401 arscp_extract(struct list *list)
402 {
403
404 if (!arscp_target_exist())
405 return;
406 arscp_mlist2argv(list);
407 ar_read_archive(bsdar, 'x', stdout);
408 arscp_free_argv();
409 arscp_free_mlist(list);
410 }
411
412 /* List modules of archive. (Simple Mode) */
413 static void
arscp_list(void)414 arscp_list(void)
415 {
416
417 if (!arscp_target_exist())
418 return;
419 bsdar->argc = 0;
420 bsdar->argv = NULL;
421 /* Always verbose. */
422 bsdar->options |= AR_V;
423 ar_read_archive(bsdar, 't', stdout);
424 bsdar->options &= ~AR_V;
425 }
426
427 /* List modules of archive. (Advance Mode) */
428 static void
arscp_dir(char * archive,struct list * list,char * rlt)429 arscp_dir(char *archive, struct list *list, char *rlt)
430 {
431 FILE *out;
432
433 /* If rlt != NULL, redirect output to it */
434 out = stdout;
435 if (rlt) {
436 if ((out = fopen(rlt, "w")) == NULL)
437 bsdar_errc(bsdar, errno, "fopen %s failed", rlt);
438 }
439
440 bsdar->filename = archive;
441 if (list)
442 arscp_mlist2argv(list);
443 else {
444 bsdar->argc = 0;
445 bsdar->argv = NULL;
446 }
447 if (verbose)
448 bsdar->options |= AR_V;
449 ar_read_archive(bsdar, 't', out);
450 bsdar->options &= ~AR_V;
451
452 if (rlt) {
453 if (fclose(out) == EOF)
454 bsdar_errc(bsdar, errno, "fclose %s failed", rlt);
455 free(rlt);
456 }
457 free(archive);
458 bsdar->filename = tmpac;
459 arscp_free_argv();
460 arscp_free_mlist(list);
461 }
462
463
464 /* Replace modules of current archive. */
465 static void
arscp_replace(struct list * list)466 arscp_replace(struct list *list)
467 {
468
469 if (!arscp_target_exist())
470 return;
471 arscp_mlist2argv(list);
472 ar_write_archive(bsdar, 'r');
473 arscp_free_argv();
474 arscp_free_mlist(list);
475 }
476
477 /* Rename the temporary archive to the target archive. */
478 static void
arscp_save(void)479 arscp_save(void)
480 {
481 mode_t mask;
482
483 if (target) {
484 if (rename(tmpac, target) < 0)
485 bsdar_errc(bsdar, errno, "rename failed");
486 /*
487 * mkstemp creates temp files with mode 0600, here we
488 * set target archive mode per process umask.
489 */
490 mask = umask(0);
491 umask(mask);
492 if (chmod(target, 0666 & ~mask) < 0)
493 bsdar_errc(bsdar, errno, "chmod failed");
494 free(tmpac);
495 free(target);
496 tmpac = NULL;
497 target= NULL;
498 bsdar->filename = NULL;
499 } else
500 bsdar_warnc(bsdar, 0, "no open output archive");
501 }
502
503 /*
504 * Discard all the contents of current archive. This is achieved by
505 * invoking CREATE cmd on current archive.
506 */
507 static void
arscp_clear(void)508 arscp_clear(void)
509 {
510 char *new_target;
511
512 if (target) {
513 new_target = strdup(target);
514 if (new_target == NULL)
515 bsdar_errc(bsdar, errno, "strdup failed");
516 arscp_create(NULL, new_target);
517 }
518 }
519
520 /*
521 * Quit ar(1). Note that END cmd will not SAVE current archive
522 * before exit.
523 */
524 static void
arscp_end(int eval)525 arscp_end(int eval)
526 {
527
528 if (target)
529 free(target);
530 if (tmpac) {
531 if (unlink(tmpac) == -1)
532 bsdar_errc(bsdar, errno, "unlink %s failed",
533 tmpac);
534 free(tmpac);
535 }
536
537 exit(eval);
538 }
539
540 /*
541 * Check if target specified, i.e, whether OPEN or CREATE has been
542 * issued by user.
543 */
544 static int
arscp_target_exist(void)545 arscp_target_exist(void)
546 {
547
548 if (target)
549 return (1);
550
551 bsdar_warnc(bsdar, 0, "no open output archive");
552 return (0);
553 }
554
555 /* Construct module list. */
556 static struct list *
arscp_mlist(struct list * list,char * str)557 arscp_mlist(struct list *list, char *str)
558 {
559 struct list *l;
560
561 l = malloc(sizeof(*l));
562 if (l == NULL)
563 bsdar_errc(bsdar, errno, "malloc failed");
564 l->str = str;
565 l->next = list;
566
567 return (l);
568 }
569
570 /* Calculate the length of a mlist. */
571 static int
arscp_mlist_len(struct list * list)572 arscp_mlist_len(struct list *list)
573 {
574 int len;
575
576 for(len = 0; list; list = list->next)
577 len++;
578
579 return (len);
580 }
581
582 /* Free the space allocated for mod_list. */
583 static void
arscp_free_mlist(struct list * list)584 arscp_free_mlist(struct list *list)
585 {
586 struct list *l;
587
588 /* Note that list->str was freed in arscp_free_argv. */
589 for(; list; list = l) {
590 l = list->next;
591 free(list);
592 }
593 }
594
595 /* Convert mlist to argv array. */
596 static void
arscp_mlist2argv(struct list * list)597 arscp_mlist2argv(struct list *list)
598 {
599 char **argv;
600 int i, n;
601
602 n = arscp_mlist_len(list);
603 argv = malloc(n * sizeof(*argv));
604 if (argv == NULL)
605 bsdar_errc(bsdar, errno, "malloc failed");
606
607 /* Note that module names are stored in reverse order in mlist. */
608 for(i = n - 1; i >= 0; i--, list = list->next) {
609 if (list == NULL)
610 bsdar_errc(bsdar, errno, "invalid mlist");
611 argv[i] = list->str;
612 }
613
614 bsdar->argc = n;
615 bsdar->argv = argv;
616 }
617
618 /* Free space allocated for argv array and its elements. */
619 static void
arscp_free_argv(void)620 arscp_free_argv(void)
621 {
622 int i;
623
624 for(i = 0; i < bsdar->argc; i++)
625 free(bsdar->argv[i]);
626
627 free(bsdar->argv);
628 }
629
630 /* Show a prompt if we are in interactive mode */
631 static void
arscp_prompt(void)632 arscp_prompt(void)
633 {
634
635 if (interactive) {
636 printf("AR >");
637 fflush(stdout);
638 }
639 }
640
641 /* Main function for ar script mode. */
642 void
ar_mode_script(struct bsdar * ar)643 ar_mode_script(struct bsdar *ar)
644 {
645
646 bsdar = ar;
647 interactive = isatty(fileno(stdin));
648 while(yyparse()) {
649 if (!interactive)
650 arscp_end(EXIT_FAILURE);
651 }
652
653 /* Script ends without END */
654 arscp_end(EXIT_SUCCESS);
655 }
656