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 ("CDDL"), version 1.0.
6 * You may use this file only in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.opensource.org/licenses/cddl1.txt
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 /*
23 * Copyright 2015-2020 J. Schilling
24 *
25 * @(#)add.c 1.1 20/05/09 J. Schilling
26 */
27 #if defined(sun)
28 #pragma ident "@(#)add.c 1.1 20/05/09 J. Schilling"
29 #endif
30 #include <defines.h>
31 #include <version.h>
32 #include <i18n.h>
33 #include <schily/stat.h>
34 #include <schily/getopt.h>
35 #include <schily/sysexits.h>
36 #include <schily/schily.h>
37 #include "sccs.h"
38
39 /*
40 * Code to support project enhancements for SCCS.
41 * This is mainly the project set home directory, the directory ".sccs" in that
42 * directory and the changeset file.
43 */
44 static char **anames;
45 static int alen;
46 static int anum;
47 static int addfile __PR((char *file));
48 static void free_anames __PR((void));
49 static int addcmp __PR((const void *file1, const void *file2));
50 static void cvtpath __PR((char *nm, char *nbuf, size_t nbsize,
51 int cwd, int phome));
52
53 EXPORT char **
sccs_getanames()54 sccs_getanames()
55 {
56 return (anames);
57 }
58
59 EXPORT int
sccs_getanum()60 sccs_getanum()
61 {
62 return (anum);
63 }
64
65 /*
66 * Add a single file name to the array of files that describes the project.
67 */
68 static int
addfile(file)69 addfile(file)
70 char *file;
71 {
72 if (alen <= anum) {
73 alen += 128;
74 anames = realloc(anames, alen * sizeof (char *));
75 if (anames == NULL)
76 efatal("out of space (ut9)");
77 }
78 if ((anames[anum++] = strdup(file)) == NULL)
79 efatal("out of space (ut9)");
80 return (0);
81 }
82
83 /*
84 * Free old strings from anames[].
85 */
86 static void
free_anames()87 free_anames()
88 {
89 int i;
90
91 for (i = 0; i < anum; i++) {
92 free(anames[i]);
93 }
94 free(anames);
95 anames = NULL;
96 alen = 0;
97 }
98
99 /*
100 * Read the current name cache file and keep it in an in core array.
101 */
102 EXPORT int
sccs_readncache()103 sccs_readncache()
104 {
105 char nbuf[FILESIZE];
106 char lbuf[MAXLINE];
107 FILE *nfp;
108
109 strlcpy(nbuf, setrhome, sizeof (nbuf));
110 strlcat(nbuf, "/.sccs/ncache", sizeof (nbuf));
111 nfp = fopen(nbuf, "rb");
112 if (nfp == NULL) {
113 return (-1);
114 }
115
116 /*
117 * Reset old entries in anames[].
118 * Then read in the file.
119 */
120 free_anames();
121 while (fgets(lbuf, sizeof (lbuf), nfp) != NULL) {
122 size_t llen = strlen(lbuf);
123
124 if (llen > 0 && lbuf[llen-1] == '\n')
125 lbuf[llen-1] = '\0';
126 addfile(lbuf);
127 }
128 fclose(nfp);
129 return (0);
130 }
131
132 /*
133 * Add compare function for qsort().
134 * Note that we do not compare the time_t field and thus use an offset of 15,
135 * see below for the format used for this string.
136 */
137 static int
addcmp(file1,file2)138 addcmp(file1, file2)
139 const void *file1;
140 const void *file2;
141 {
142 int ret = strcmp(&(*(char **)file1)[15], &(*(char **)file2)[15]);
143
144 if (ret == 0) {
145 Ffile = &(*(char **)file1)[15];
146 fatal(gettext("already tracked (sc4)"));
147 }
148 return (ret);
149 }
150
151 /*
152 * Convert a path name that is relative to the current working directory into
153 * a path name that is relative to the change set home directory and normalize
154 * the resulting path name.
155 */
156 static void
cvtpath(nm,nbuf,nbsize,cwd,phome)157 cvtpath(nm, nbuf, nbsize, cwd, phome)
158 char *nm; /* The file name argument */
159 char *nbuf; /* The file name output buffer */
160 size_t nbsize; /* The size of the file name output buffer */
161 int cwd; /* File descriptor to working dir */
162 int phome; /* File descriptor to change set home dir */
163 {
164 char npbuf[FILESIZE];
165 size_t npboff;
166 int nlen;
167 char *name;
168
169 name = nm;
170 npboff = 0;
171 if (cwdprefix[0] && name[0] != '/') {
172 /*
173 * We are not in the change set home directory and
174 * "name" is not an absolute path name.
175 * Copy the cwd prefix before the path to get a path
176 * name that is relative to the the change set home.
177 */
178 strlcpy(&npbuf[npboff], cwdprefix, sizeof (npbuf) - npboff);
179 npboff += cwdprefixlen;
180 strlcpy(&npbuf[npboff], "/", sizeof (npbuf) - npboff);
181 npboff += 1;
182 strlcpy(&npbuf[npboff], name, sizeof (npbuf) - npboff);
183 name = npbuf;
184 }
185 /*
186 * Since all our path names are relative to the change set
187 * home directory, we need to fchdir() to that directory before
188 * we normalize the path name.
189 */
190 if (*nm != '/' && fchdir(phome) < 0) {
191 Ffile = setahome ? setahome : setrhome;
192 efatal("cannot change directory (cm16)");
193 }
194 nlen = resolvepath(name, nbuf, nbsize); /* Must exist */
195 if (nlen < 0) {
196 efatal("path conversion error (cm12)");
197 } else if (nlen >= nbsize) {
198 fatal("resolved path too long (cm13)");
199 } else {
200 /*
201 * While the libschily implementation null terminates
202 * the names, this is not the case for the Solaris
203 * syscall resolvepath().
204 */
205 nbuf[nlen] = '\0';
206
207 /*
208 * If the resulting name is an absolute path that starts
209 * with the absolute change set home directory string,
210 * try to make it relative by removing the absolute home
211 * path string.
212 *
213 * If the remaining path is not inside the change set
214 * home tree, abort.
215 */
216 if (nbuf[0] == '/' && setahome)
217 make_relative(nbuf);
218 if (!in_tree(nbuf)) {
219 Ffile = nbuf;
220 fatal("not in tree (cm17)");
221 }
222 }
223 /*
224 * Chdir() back to our previous working directory since all
225 * file arguments are relative to that directory.
226 */
227 if (*nm != '/' && fchdir(cwd) < 0) {
228 Ffile = ".";
229 efatal("cannot change directory (cm16)");
230 }
231 }
232
233 /*
234 * Add file to the current file set for the current change set.
235 * This command always needs to have file type argument(s).
236 */
237 EXPORT int
addcmd(nfiles,argc,argv)238 addcmd(nfiles, argc, argv)
239 int nfiles;
240 int argc;
241 char **argv;
242 {
243 char nbuf[FILESIZE];
244 size_t nboff;
245 int rval;
246 char **np;
247 int files = 0;
248 int cwd = -1; /* File descriptor to cwd */
249 int phome = -1; /* File descriptor to project home */
250 struct stat statb;
251
252 optind = 1;
253 opt_sp = 1;
254 while ((rval = getopt(argc, argv, "")) != -1) {
255 switch (rval) {
256
257 default:
258 usrerr("%s %s",
259 gettext("unknown option"),
260 argv[optind-1]);
261 rval = EX_USAGE;
262 exit(EX_USAGE);
263 /*NOTREACHED*/
264 }
265 }
266
267 rval = 0;
268 for (np = &argv[optind]; *np != NULL; np++) {
269 if (files == 0) {
270 /*
271 * In order to keep the first implementation simple,
272 * we assume that the current working directory is
273 * always inside the change set tree and that all
274 * file arguments are from the same change set.
275 *
276 * XXX If we like to enhance this, we first need to
277 * XXX find what we like to support.
278 */
279 checkhome(NULL); /* No project set home: abort */
280 sccs_readncache(); /* Read already known files */
281
282 cwd = opencwd();
283 phome = openphome();
284 }
285 files |= 1;
286 /*
287 * 12 digits work from year -1199 up to year 33658 but we may
288 * need to check for strange time stamps if time_t has more
289 * that 32 bits since such a strange time stamp could cause
290 * an overflow in the string below.
291 */
292 strlcpy(nbuf, "A 000000000000 ", sizeof (nbuf));
293 if (stat(*np, &statb) < 0)
294 xmsg(*np, NOGETTEXT("add"));
295 #if SIZEOF_TIME_T > 4
296 if (statb.st_mtime > 999999999999L ||
297 statb.st_mtime < -99999999999L)
298 efatal("file not in supported time range (cm15)");
299 #endif
300 sprintf(&nbuf[2], "%12ld ", (long)statb.st_mtime);
301 nboff = 15;
302 cvtpath(*np, &nbuf[nboff], sizeof (nbuf) - nboff, cwd, phome);
303 addfile(nbuf);
304 }
305 closedirfd(cwd);
306 closedirfd(phome);
307
308 if (files == 0) {
309 usrerr(gettext(" missing file arg (cm3)"));
310 } else {
311 FILE *nfp;
312 int i;
313
314 qsort((void *)anames, anum, sizeof (char *), addcmp);
315
316 strlcpy(nbuf, setrhome, sizeof (nbuf));
317 strlcat(nbuf, "/.sccs/ncache", sizeof (nbuf));
318 nfp = xfcreat(nbuf, 0666);
319 for (i = 0; i < anum; i++) {
320 fputs(anames[i], nfp);
321 fputs("\n", nfp);
322 }
323 fclose(nfp);
324 }
325 return (rval);
326 }
327