1/* See LICENSE file for copyright and license details. */
2
3
4#include <grp.h>
5#include <pwd.h>
6#include <errno.h>
7#include <fcntl.h>
8#include <unistd.h>
9#include <stdlib.h>
10#include <string.h>
11#include <dirent.h>
12#include <sys/stat.h>
13#include <sys/wait.h>
14
15#include "util.h"
16
17static int Dflag = 0;
18static gid_t group;
19static uid_t owner;
20static mode_t mode = 0755;
21
22static void
23make_dir(char *dir, int was_missing)
24{
25 if (!mkdir(dir, was_missing ? 0755 : mode)) {
26 if (!was_missing && (lchown(dir, owner, group) < 0))
27 eprintf("lchmod %s:", dir);
28 } else if (errno != EEXIST) {
29 eprintf("mkdir %s:", dir);
30 }
31}
32
33static void
34make_dirs(char *dir, int was_missing)
35{
36 char *p;
37 for (p = strchr(dir + (dir[0] == '/'), '/'); p; p = strchr(p + 1, '/')) {
38 *p = '\0';
39 make_dir(dir, was_missing);
40 *p = '/';
41 }
42 make_dir(dir, was_missing);
43}
44
45static int
46install(const char *s1, const char *s2, int depth)
47{
48 int f1, f2;
49
50 (void)depth;
51
52 if ((f1 = open(s1, O_RDONLY)) < 0)
53 eprintf("open %s:", s1);
54 if ((f2 = creat(s2, 0600)) < 0) {
55 if (unlink(s2) < 0 && errno != ENOENT)
56 eprintf("unlink %s:", s2);
57 if ((f2 = creat(s2, 0600)) < 0)
58 eprintf("creat %s:", s2);
59 }
60 if (concat(f1, s1, f2, s2) < 0)
61 goto fail;
62 if (fchmod(f2, mode) < 0) {
63 weprintf("fchmod %s:", s2);
64 goto fail;
65 }
66 if (fchown(f2, owner, group) < 0) {
67 weprintf("fchown %s:", s2);
68 goto fail;
69 }
70
71 close(f1);
72 close(f2);
73
74 return 0;
75
76fail:
77 unlink(s2);
78 exit(1);
79}
80
81static void
82usage(void)
83{
84 eprintf("usage: %s [-g group] [-o owner] [-m mode] (-d dir ... | [-D] (-t dest source ... | source ... dest))\n", argv0);
85}
86
87// ?man xinstall: copy files and set attributes
88// ?man arguments: (-d dir ... | (-t dest source ... | source ... dest))
89// ?man copy files and set their permissions and ownership
90int
91main(int argc, char *argv[])
92{
93 int dflag = 0;
94 char *gflag = 0;
95 char *oflag = 0;
96 char *mflag = 0;
97 char *tflag = 0;
98 struct group *gr;
99 struct passwd *pw;
100 struct stat st;
101 char *p;
102
103 ARGBEGIN {
104 // ?man -c: print count or perform stdout action
105 case 'c':
106 /* no-op for compatibility */
107 break;
108 // ?man -d: specify directory
109 case 'd':
110 dflag = 1;
111 break;
112 // ?man -D: specify option flag
113 case 'D':
114 Dflag = 1;
115 break;
116 // ?man -s: silent mode or print summary
117 case 's':
118 /* no-op for compatibility */
119 break;
120 // ?man -g:str: specify option flag
121 case 'g':
122 gflag = EARGF(usage());
123 break;
124 // ?man -o:str: specify output file
125 case 'o':
126 oflag = EARGF(usage());
127 break;
128 // ?man -m:str: specify mode or limit
129 case 'm':
130 mflag = EARGF(usage());
131 break;
132 // ?man -t:str: sort or specify timestamp
133 case 't':
134 tflag = EARGF(usage());
135 break;
136 default:
137 usage();
138 } ARGEND
139
140 if (argc < 1 + (!tflag & !dflag) || dflag & (Dflag | !!tflag))
141 usage();
142
143 if (gflag) {
144 errno = 0;
145 gr = getgrnam(gflag);
146 if (gr) {
147 group = gr->gr_gid;
148 } else {
149 if (errno)
150 eprintf("getgrnam %s:", gflag);
151 group = estrtonum(gflag, 0, UINT_MAX);
152 }
153 } else {
154 group = getgid();
155 }
156
157 if (oflag) {
158 errno = 0;
159 pw = getpwnam(oflag);
160 if (pw) {
161 owner = pw->pw_uid;
162 } else {
163 if (errno)
164 eprintf("getpwnam %s:", oflag);
165 owner = estrtonum(oflag, 0, UINT_MAX);
166 }
167 } else {
168 owner = getuid();
169 }
170
171 if (mflag)
172 mode = parsemode(mflag, mode, 0);
173
174 if (dflag) {
175 for (; *argv; argc--, argv++)
176 make_dirs(*argv, 0);
177 return 0;
178 }
179
180 if (tflag) {
181 argv = memmove(argv - 1, argv, argc * sizeof(*argv));
182 argv[argc++] = tflag;
183 }
184 if (tflag || argc > 2) {
185 if (stat(argv[argc - 1], &st) < 0) {
186 if ((errno == ENOENT) && Dflag) {
187 make_dirs(argv[argc - 1], 1);
188 } else {
189 eprintf("stat %s:", argv[argc - 1]);
190 }
191 } else if (!S_ISDIR(st.st_mode)) {
192 eprintf("%s: not a directory\n", argv[argc - 1]);
193 }
194 }
195 if (stat(argv[argc - 1], &st) < 0) {
196 if (errno != ENOENT)
197 eprintf("stat %s:", argv[argc - 1]);
198 if (tflag || Dflag || argc > 2) {
199 if ((p = strrchr(argv[argc - 1], '/')) != NULL) {
200 *p = '\0';
201 make_dirs(argv[argc - 1], 1);
202 *p = '/';
203 }
204 }
205 }
206 enmasse(argc, argv, install);
207
208 return 0;
209}