]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/ar/acpyacc.y
Remove $FreeBSD$: one-line sh pattern
[FreeBSD/FreeBSD.git] / usr.bin / ar / acpyacc.y
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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 }