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