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 /*
22 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * Copyright (c) 1988 AT&T
27 * All Rights Reserved
28 *
29 */
30
31 /*
32 * Incompatible Archive Header
33 *
34 * The archive file member header used in SunOS 4.1 archive files and
35 * Solaris archive files are incompatible. The header file is:
36 * /usr/include/ar.h, struct ar_hdr.
37 * The member ar_name[] in Solaris comforms with Standard and the
38 * member name terminates with '/'. The SunOS's member does not terminate
39 * with '/' character. A bug 4046054 was filed:
40 * The ar command in Solaris 2.5.1 is incompatible with archives
41 * created on 4.x.
42 *
43 * To handle archive files created in SunOS 4.1 system on Solaris, the
44 * following changes were made:
45 *
46 * 1. file.c/writefile()
47 * Before writing each member files into the output
48 * archive file, ar_name[] is checked. If it is NULL,
49 * it means that the original archive header for this
50 * member was incompatible with Solaris format.
51 *
52 * The original Solaris ar command ended up having
53 * NULL name for the header. The change here uses the
54 * ar_rawname, which is much closer to the original
55 * name.
56 *
57 * 2. cmd.c
58 * For the p command, the code used to use only ar_longname
59 * to seach the matching name. The member is set to NULL
60 * if the archive member header was incompatible.
61 * The ar_rawname is also used to find the matching member name.
62 *
63 * For commands to update the archive file, we do not
64 * use ar_rawname, and just use the ar_longname. The commands are
65 * r (replace), m (modify the position) and d (delete).
66 */
67
68 #include "inc.h"
69
70 /*
71 * Forward Declarations
72 */
73 static void ar_select(int *, unsigned long);
74 static void cleanup(Cmd_info *);
75 static int create_extract(ARFILE *, int, int, Cmd_info *);
76 static char *match(char *, Cmd_info *);
77 static void mesg(int, char *, Cmd_info *);
78 static void movefil(ARFILE *, struct stat *);
79 static FILE *stats(char *, struct stat *);
80
81 /*
82 * Commands
83 */
84 void
rcmd(Cmd_info * cmd_info)85 rcmd(Cmd_info *cmd_info)
86 {
87 FILE *f;
88 ARFILE *fileptr;
89 ARFILE *abifile = NULL;
90 ARFILE *backptr = NULL;
91 ARFILE *endptr;
92 ARFILE *moved_files;
93 ARFILE *prev_entry, *new_listhead, *new_listend;
94 int deleted;
95 struct stat stbuf;
96 char *gfile;
97
98 new_listhead = NULL;
99 new_listend = NULL;
100 prev_entry = NULL;
101
102 for (fileptr = getfile(cmd_info);
103 fileptr; fileptr = getfile(cmd_info)) {
104 deleted = 0;
105 if (!abifile && cmd_info->ponam &&
106 strcmp(fileptr->ar_longname, cmd_info->ponam) == 0)
107 abifile = fileptr;
108 else if (!abifile)
109 backptr = fileptr;
110
111 if (cmd_info->namc == 0 ||
112 (gfile = match(fileptr->ar_longname, cmd_info)) != NULL) {
113 /*
114 * NOTE:
115 * Refer to "Incompatible Archive Header"
116 * blocked comment at the beginning of this file.
117 */
118 f = stats(gfile, &stbuf); /* gfile is set by match */
119 if (f == NULL) {
120 if (cmd_info->namc) {
121 int err = errno;
122 (void) fprintf(stderr,
123 MSG_INTL(MSG_SYS_OPEN),
124 gfile, strerror(err));
125 }
126 /*
127 * Created
128 */
129 mesg('c', gfile, cmd_info);
130 } else {
131 if ((cmd_info->opt_flgs & u_FLAG) &&
132 stbuf.st_mtime <= fileptr->ar_date) {
133 (void) fclose(f);
134 continue;
135 }
136 /*
137 * Replaced
138 */
139 mesg('r', fileptr->ar_longname, cmd_info);
140 movefil(fileptr, &stbuf);
141 /*
142 * Clear the previous contents.
143 */
144 if (fileptr->ar_flag & F_ELFRAW) {
145 /*
146 * clear ar_elf
147 */
148 (void) elf_end(fileptr->ar_elf);
149 fileptr->ar_elf = 0;
150 }
151 /* clear 'ar_flag' */
152 fileptr->ar_flag &= ~F_ELFRAW;
153
154 /*
155 * Defer reading contents until needed, and
156 * then use an in-kernel file-to-file transfer
157 * to avoid excessive in-process memory use.
158 */
159 fileptr->ar_contents = NULL;
160
161 if (fileptr->ar_pathname != NULL)
162 free(fileptr->ar_pathname);
163 if ((fileptr->ar_pathname =
164 malloc(strlen(gfile) + 1)) == NULL) {
165 int err = errno;
166 (void) fprintf(stderr,
167 MSG_INTL(MSG_MALLOC),
168 strerror(err));
169 exit(1);
170 }
171
172 (void) strcpy(fileptr->ar_pathname, gfile);
173 (void) fclose(f);
174
175 if (cmd_info->ponam && (abifile != fileptr)) {
176 deleted = 1;
177 /* remove from archive list */
178 if (prev_entry != NULL)
179 prev_entry->ar_next = NULL;
180 else
181 listhead = NULL;
182 listend = prev_entry;
183
184 /* add to moved list */
185 if (new_listhead == NULL)
186 new_listhead = fileptr;
187 else
188 new_listend->ar_next = fileptr;
189 new_listend = fileptr;
190 }
191 cmd_info->modified++;
192 }
193 }
194 else
195 /*
196 * Unchaged
197 */
198 mesg('u', fileptr->ar_longname, cmd_info);
199
200 if (deleted)
201 deleted = 0;
202 else
203 prev_entry = fileptr;
204 }
205
206 endptr = listend;
207 cleanup(cmd_info);
208 if (cmd_info->ponam && endptr &&
209 (((moved_files = endptr->ar_next) != NULL) || new_listhead)) {
210 if (!abifile) {
211 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
212 cmd_info->ponam);
213 exit(2);
214 }
215 endptr->ar_next = NULL;
216
217 /*
218 * link new/moved files into archive entry list...
219 * 1: prepend newlist to moved/appended list
220 */
221 if (new_listhead) {
222 if (!moved_files)
223 listend = new_listend;
224 new_listend->ar_next = moved_files;
225 moved_files = new_listhead;
226 }
227 /* 2: insert at appropriate position... */
228 if (cmd_info->opt_flgs & b_FLAG)
229 abifile = backptr;
230 if (abifile) {
231 listend->ar_next = abifile->ar_next;
232 abifile->ar_next = moved_files;
233 } else {
234 listend->ar_next = listhead;
235 listhead = moved_files;
236 }
237 listend = endptr;
238 } else if (cmd_info->ponam && !abifile)
239 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
240 cmd_info->ponam);
241 }
242
243 void
dcmd(Cmd_info * cmd_info)244 dcmd(Cmd_info *cmd_info)
245 {
246 ARFILE *fptr;
247 ARFILE *backptr = NULL;
248
249 for (fptr = getfile(cmd_info); fptr; fptr = getfile(cmd_info)) {
250 if (match(fptr->ar_longname, cmd_info) != NULL) {
251 /*
252 * NOTE:
253 * Refer to "Incompatible Archive Header"
254 * blocked comment at the beginning of this file.
255 */
256
257 /*
258 * Deleted
259 */
260 mesg('d', fptr->ar_longname, cmd_info);
261 if (backptr == NULL) {
262 listhead = NULL;
263 listend = NULL;
264 } else {
265 backptr->ar_next = NULL;
266 listend = backptr;
267 }
268 cmd_info->modified = 1;
269 } else {
270 /*
271 * Unchaged
272 */
273 mesg('u', fptr->ar_longname, cmd_info);
274 backptr = fptr;
275 }
276 }
277 }
278
279 void
xcmd(Cmd_info * cmd_info)280 xcmd(Cmd_info *cmd_info)
281 {
282 int f;
283 ARFILE *next;
284 int rawname = 0;
285 long f_len = 0;
286
287 /*
288 * If -T is specified, get the maximum file name length.
289 */
290 if (cmd_info->opt_flgs & T_FLAG) {
291 f_len = pathconf(MSG_ORIG(MSG_STR_PERIOD), _PC_NAME_MAX);
292 if (f_len == -1) {
293 int err = errno;
294 (void) fprintf(stderr, MSG_INTL(MSG_PATHCONF),
295 strerror(err));
296 exit(1);
297 }
298 }
299 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
300 if ((next->ar_longname[0] == 0) && (next->ar_rawname[0] != 0))
301 rawname = 1;
302 if (cmd_info->namc == 0 ||
303 match(next->ar_longname, cmd_info) != NULL ||
304 match(next->ar_rawname, cmd_info) != NULL) {
305 /*
306 * NOTE:
307 * Refer to "Incompatible Archive Header"
308 * blocked comment at the beginning of this file.
309 */
310 f = create_extract(next, rawname, f_len, cmd_info);
311 if (f >= 0) {
312 if (rawname) {
313 /*
314 * eXtracted
315 */
316 mesg('x', next->ar_rawname, cmd_info);
317 if (write(f, next->ar_contents,
318 (unsigned)next->ar_size) !=
319 next->ar_size) {
320 int err = errno;
321 (void) fprintf(stderr,
322 MSG_INTL(MSG_SYS_WRITE),
323 next->ar_rawname,
324 strerror(err));
325 exit(1);
326 }
327 } else {
328 /*
329 * eXtracted
330 */
331 mesg('x', next->ar_longname, cmd_info);
332 if (write(f, next->ar_contents,
333 (unsigned)next->ar_size) !=
334 next->ar_size) {
335 int err = errno;
336 (void) fprintf(stderr,
337 MSG_INTL(MSG_SYS_WRITE),
338 next->ar_longname,
339 strerror(err));
340 exit(1);
341 }
342 }
343 (void) close(f);
344 } else
345 exit(1);
346 }
347 rawname = 0;
348 } /* for */
349 }
350
351 void
pcmd(Cmd_info * cmd_info)352 pcmd(Cmd_info *cmd_info)
353 {
354 ARFILE *next;
355
356 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
357 if (cmd_info->namc == 0 ||
358 match(next->ar_longname, cmd_info) != NULL ||
359 match(next->ar_rawname, cmd_info) != NULL) {
360 /*
361 * NOTE:
362 * Refer to "Incompatible Archive Header"
363 * blocked comment at the beginning of this file.
364 */
365 if (cmd_info->opt_flgs & v_FLAG) {
366 (void) fprintf(stdout,
367 MSG_ORIG(MSG_FMT_P_TITLE),
368 next->ar_longname);
369 (void) fflush(stdout);
370 }
371 (void) fwrite(next->ar_contents, sizeof (char),
372 next->ar_size, stdout);
373 }
374 }
375 }
376
377 void
mcmd(Cmd_info * cmd_info)378 mcmd(Cmd_info *cmd_info)
379 {
380 ARFILE *fileptr;
381 ARFILE *abifile = NULL;
382 ARFILE *tmphead = NULL;
383 ARFILE *tmpend = NULL;
384 ARFILE *backptr1 = NULL;
385 ARFILE *backptr2 = NULL;
386
387 for (fileptr = getfile(cmd_info);
388 fileptr; fileptr = getfile(cmd_info)) {
389 if (match(fileptr->ar_longname, cmd_info) != NULL) {
390 /*
391 * position Modified
392 */
393 mesg('m', fileptr->ar_longname, cmd_info);
394 if (tmphead)
395 tmpend->ar_next = fileptr;
396 else
397 tmphead = fileptr;
398 tmpend = fileptr;
399 if (backptr1) {
400 listend = backptr1;
401 listend->ar_next = NULL;
402 }
403 else
404 listhead = NULL;
405 continue;
406 }
407 /*
408 * position Unchaged
409 */
410 mesg('u', fileptr->ar_longname, cmd_info);
411 backptr1 = fileptr;
412 if (cmd_info->ponam && !abifile) {
413 if (strcmp(fileptr->ar_longname, cmd_info->ponam) == 0)
414 abifile = fileptr;
415 else
416 backptr2 = fileptr;
417 }
418 }
419
420 if (!tmphead)
421 return;
422
423 if (!cmd_info->ponam)
424 listend->ar_next = tmphead;
425 else {
426 if (!abifile) {
427 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
428 cmd_info->ponam);
429 exit(2);
430 }
431 if (cmd_info->opt_flgs & b_FLAG)
432 abifile = backptr2;
433 if (abifile) {
434 tmpend->ar_next = abifile->ar_next;
435 abifile->ar_next = tmphead;
436 } else {
437 tmphead->ar_next = listhead;
438 listhead = tmphead;
439 }
440 }
441 (cmd_info->modified)++;
442 }
443
444 void
tcmd(Cmd_info * cmd_info)445 tcmd(Cmd_info *cmd_info)
446 {
447 ARFILE *next;
448 int **mp;
449 char buf[DATESIZE];
450 int m1[] = {1, S_IRUSR, 'r', '-'};
451 int m2[] = {1, S_IWUSR, 'w', '-'};
452 int m3[] = {2, S_ISUID, 's', S_IXUSR, 'x', '-'};
453 int m4[] = {1, S_IRGRP, 'r', '-'};
454 int m5[] = {1, S_IWGRP, 'w', '-'};
455 int m6[] = {2, S_ISGID, 's', S_IXGRP, 'x', '-'};
456 int m7[] = {1, S_IROTH, 'r', '-'};
457 int m8[] = {1, S_IWOTH, 'w', '-'};
458 int m9[] = {2, S_ISVTX, 't', S_IXOTH, 'x', '-'};
459 int *m[10];
460
461 m[0] = m1;
462 m[1] = m2;
463 m[2] = m3;
464 m[3] = m4;
465 m[4] = m5;
466 m[5] = m6;
467 m[6] = m7;
468 m[7] = m8;
469 m[8] = m9;
470 m[9] = 0;
471
472 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
473 if (cmd_info->namc == 0 ||
474 match(next->ar_longname, cmd_info) != NULL ||
475 match(next->ar_rawname, cmd_info) != NULL) {
476 /*
477 * NOTE:
478 * Refer to "Incompatible Archive Header"
479 * blocked comment at the beginning of this file.
480 */
481 if (cmd_info->opt_flgs & v_FLAG) {
482 for (mp = &m[0]; mp < &m[9]; )
483 ar_select(*mp++, next->ar_mode);
484
485 (void) fprintf(stdout, MSG_ORIG(MSG_FMT_T_IDSZ),
486 next->ar_uid, next->ar_gid,
487 EC_XWORD(next->ar_size));
488 if ((strftime(buf,
489 DATESIZE, MSG_ORIG(MSG_FMT_T_DATE),
490 localtime(&(next->ar_date)))) == 0) {
491 (void) fprintf(stderr,
492 MSG_INTL(MSG_LOCALTIME));
493 exit(1);
494 }
495 (void) fprintf(stdout,
496 MSG_ORIG(MSG_FMT_SPSTRSP), buf);
497 }
498 if ((next->ar_longname[0] == 0) &&
499 (next->ar_rawname[0] != 0))
500 (void) fprintf(stdout,
501 MSG_ORIG(MSG_FMT_STRNL),
502 trim(next->ar_rawname));
503 else
504 (void) fprintf(stdout,
505 MSG_ORIG(MSG_FMT_STRNL),
506 trim(next->ar_longname));
507 }
508 } /* for */
509 }
510
511 void
qcmd(Cmd_info * cmd_info)512 qcmd(Cmd_info *cmd_info)
513 {
514 ARFILE *fptr;
515
516 if (cmd_info->opt_flgs & (a_FLAG | b_FLAG)) {
517 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_05));
518 exit(1);
519 }
520 for (fptr = getfile(cmd_info); fptr; fptr = getfile(cmd_info))
521 ;
522 cleanup(cmd_info);
523 }
524
525 /*
526 * Supplementary functions
527 */
528 static char *
match(char * file,Cmd_info * cmd_info)529 match(char *file, Cmd_info *cmd_info)
530 {
531 int i;
532
533 for (i = 0; i < cmd_info->namc; i++) {
534 if (cmd_info->namv[i] == 0)
535 continue;
536 if (strcmp(trim(cmd_info->namv[i]), file) == 0) {
537 file = cmd_info->namv[i];
538 cmd_info->namv[i] = 0;
539 return (file);
540 }
541 }
542 return (NULL);
543 }
544
545 /*
546 * puts the file which was in the list in the linked list
547 */
548 static void
cleanup(Cmd_info * cmd_info)549 cleanup(Cmd_info *cmd_info)
550 {
551 int i;
552 FILE *f;
553 ARFILE *fileptr;
554 struct stat stbuf;
555
556 for (i = 0; i < cmd_info->namc; i++) {
557 if (cmd_info->namv[i] == 0)
558 continue;
559 /*
560 * Appended
561 */
562 mesg('a', cmd_info->namv[i], cmd_info);
563 f = stats(cmd_info->namv[i], &stbuf);
564 if (f == NULL) {
565 int err = errno;
566 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
567 cmd_info->namv[i], strerror(err));
568 } else {
569 fileptr = newfile();
570 /* if short name */
571 (void) strncpy(fileptr->ar_name,
572 trim(cmd_info->namv[i]), SNAME);
573
574 if ((fileptr->ar_longname =
575 malloc(strlen(trim(cmd_info->namv[i])) + 1)) ==
576 NULL) {
577 int err = errno;
578 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
579 strerror(err));
580 exit(1);
581 }
582
583 (void) strcpy(fileptr->ar_longname,
584 trim(cmd_info->namv[i]));
585
586 if ((fileptr->ar_pathname =
587 malloc(strlen(cmd_info->namv[i]) + 1)) == NULL) {
588 int err = errno;
589 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
590 strerror(err));
591 exit(1);
592 }
593
594 (void) strcpy(fileptr->ar_pathname, cmd_info->namv[i]);
595
596 movefil(fileptr, &stbuf);
597
598 /* clear 'ar_flag' */
599 fileptr->ar_flag &= ~F_ELFRAW;
600
601 /*
602 * Defer reading contents until needed, and then use
603 * an in-kernel file-to-file transfer to avoid
604 * excessive in-process memory use.
605 */
606 fileptr->ar_contents = NULL;
607
608 (void) fclose(f);
609 (cmd_info->modified)++;
610 cmd_info->namv[i] = 0;
611 }
612 }
613 }
614
615 /*
616 * insert the file 'file' into the temporary file
617 */
618 static void
movefil(ARFILE * fileptr,struct stat * stbuf)619 movefil(ARFILE *fileptr, struct stat *stbuf)
620 {
621 fileptr->ar_size = stbuf->st_size;
622 fileptr->ar_date = stbuf->st_mtime;
623 fileptr->ar_mode = stbuf->st_mode;
624
625 /*
626 * The format of an 'ar' file includes a 6 character
627 * decimal string to contain the uid.
628 *
629 * If the uid or gid is too big to fit, then set it to
630 * nobody (for want of a better value). Clear the
631 * setuid/setgid bits in the mode to avoid setuid nobody
632 * or setgid nobody files unexpectedly coming into existence.
633 */
634 if ((fileptr->ar_uid = stbuf->st_uid) > 999999) {
635 fileptr->ar_uid = UID_NOBODY;
636 if (S_ISREG(fileptr->ar_mode))
637 fileptr->ar_mode &= ~S_ISUID;
638 }
639 if ((fileptr->ar_gid = stbuf->st_gid) > 999999) {
640 fileptr->ar_gid = GID_NOBODY;
641 if (S_ISREG(fileptr->ar_mode))
642 fileptr->ar_mode &= ~S_ISGID;
643 }
644 }
645
646 static FILE *
stats(char * file,struct stat * stbuf)647 stats(char *file, struct stat *stbuf)
648 {
649 FILE *f;
650
651 f = fopen(file, MSG_ORIG(MSG_STR_LCR));
652 if (f == NULL)
653 return (f);
654 if (stat(file, stbuf) < 0) {
655 (void) fclose(f);
656 return (NULL);
657 }
658 return (f);
659 }
660
661 /*
662 * Used by xcmd()
663 */
664 int
create_extract(ARFILE * a,int rawname,int f_len,Cmd_info * cmd_info)665 create_extract(ARFILE *a, int rawname, int f_len, Cmd_info *cmd_info)
666 {
667
668 int f;
669 char *f_name;
670 char *dup = NULL;
671 if (rawname)
672 f_name = a->ar_rawname;
673 else
674 f_name = a->ar_longname;
675
676 /*
677 * If -T is specified, check the file length.
678 */
679 if (cmd_info->opt_flgs & T_FLAG) {
680 int len;
681 len = strlen(f_name);
682 if (f_len <= len) {
683 dup = malloc(f_len+1);
684 if (dup == NULL) {
685 int err = errno;
686 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
687 strerror(err));
688 exit(1);
689 }
690 (void) strncpy(dup, f_name, f_len);
691 }
692 f_name = dup;
693 }
694
695 /*
696 * Bug 4052067 - If a file to be extracted has the same
697 * filename as the archive, the archive gets overwritten
698 * which can lead to a corrupted archive or worse, a ufs
699 * deadlock because libelf has mmap'ed the archive! We
700 * can't rely on strcmp() to test for this case because
701 * the archive could be prefixed with a partial or full
702 * path (and we could be using the rawname from the archive)
703 * This means we have to do the same thing we did for mv,
704 * which is to explicitly check if the file we would extract
705 * to is identical to the archive. Because part of this
706 * test is essentially what the -C flag does, I've merged
707 * the code together.
708 */
709 if (access(f_name, F_OK) != -1) {
710 struct stat s1, s2;
711
712 /*
713 * If -C is specified, this is an error anyway
714 */
715 if (cmd_info->opt_flgs & C_FLAG) {
716 (void) fprintf(stderr, MSG_INTL(MSG_OVERRIDE_WARN),
717 f_name);
718 if (dup != NULL)
719 free(dup);
720 return (-1);
721 }
722
723 /*
724 * Okay, -C wasn't specified. However, now we do
725 * the check to see if the archive would be overwritten
726 * by extracting this file. stat() both objects and
727 * test to see if their identical.
728 */
729 if ((stat(f_name, &s1) == 0) &&
730 (stat(cmd_info->arnam, &s2) == 0)) {
731
732 if ((s1.st_dev == s2.st_dev) &&
733 (s1.st_ino == s2.st_ino)) {
734
735 (void) fprintf(stderr,
736 MSG_INTL(MSG_OVERRIDE_WARN), f_name);
737 if (dup != NULL)
738 free(dup);
739 return (-1);
740 }
741 }
742 }
743
744 /*
745 * Okay to create extraction file...
746 */
747 f = creat(f_name, (mode_t)a->ar_mode & 0777);
748 if (f < 0) {
749 int err = errno;
750 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), f_name,
751 strerror(err));
752 /*
753 * Created
754 */
755 mesg('c', f_name, cmd_info);
756 }
757 if (dup)
758 free(dup);
759 return (f);
760 }
761
762 static void
mesg(int c,char * file,Cmd_info * cmd_info)763 mesg(int c, char *file, Cmd_info *cmd_info)
764 {
765 #ifdef XPG4
766 /*
767 * XPG4 does not have any message defined for
768 * 'c' operation.
769 * In fact, XPG only defines messages for
770 * d, r, a and x at the present. (03/05/'96)
771 */
772 if (c == 'c' || c == 'u' || c == 'm')
773 return;
774 #endif
775 /*
776 * If 'u' is passed, convert it to 'c'.
777 * 'u' makes more sense since the operation did not
778 * do anything, Unchanged, but 'c' has been used so
779 * I do no want to break the compatibility at this moment.
780 * (03/05/'96).
781 */
782 if (c == 'u')
783 c = 'c';
784 if (cmd_info->opt_flgs & v_FLAG)
785 if (c != 'c')
786 (void) fprintf(stdout, MSG_ORIG(MSG_FMT_FILE), c, file);
787 }
788
789 static void
ar_select(int * pairp,unsigned long mode)790 ar_select(int *pairp, unsigned long mode)
791 {
792 int n, *ap;
793
794 ap = pairp;
795 n = *ap++;
796 while (--n >= 0 && (mode & *ap++) == 0)
797 ap++;
798 (void) putchar(*ap);
799 }
800