mkfs: symlink support
[minix.git] / commands / ci / ci.c
blob6bcc5b51aeaf5bf8797fd5ad7ab3e933406a05e0
1 /* ci - check in Author: Peter S. Housel 12/17/87 */
3 #include <sys/types.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <pwd.h>
7 #include <signal.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <fcntl.h>
12 #include <sys/wait.h>
13 #include <stdio.h>
15 #define SUFFIX ",S" /* svc indicator */
16 #define SVCDIR "SVC" /* svc postfix indicator */
18 #define LINELEN 256 /* maximum line length */
20 #ifndef PATCH
21 #define FIX "fix $1 Fix.$1 > New.$1; mv New.$1 $1\n"
22 #else
23 #define FIX "patch -n -s $1 < Fix.$1; rm -f $1.orig\n"
24 #endif /* !PATCH */
26 #ifdef MAXPATHLEN
27 #define PATHLEN MAXPATHLEN
28 #else
29 #define PATHLEN 128 /* buffer length for filenames */
30 #endif
32 int unlocked = 0; /* leave unlocked after checkin */
33 int relock = 0; /* lock next revision after checkin */
34 char file[PATHLEN]; /* file to be checked in */
35 char svc[PATHLEN]; /* filename for svc file */
36 char newsvc[PATHLEN]; /* new copy of SVC file */
37 char line[LINELEN]; /* temporary line buffer */
38 char *p; /* scratch character pointer */
40 FILE *svcfp; /* svc file */
41 FILE *origfp, *newfp; /* "orig" and "new" temp files */
42 FILE *srcfp; /* source file */
43 int rev; /* new revision number */
44 int status; /* wait() buffer */
45 struct stat stb1, stb2; /* stat buffers for size compare */
46 char original[] = "/tmp/cioXXXXXX"; /* previous revision */
47 char diffout[] = "/tmp/cidXXXXXX"; /* diffs */
49 int main(int argc, char **argv);
50 void rundiff(void);
51 void logmsg(FILE *fp);
52 void fname(char *src, char *dst);
53 void svcname(char *src, char *dst);
54 int lockcheck(FILE *fp, int rev);
55 void onintr(int dummy);
56 void clean(void);
57 char *whoami(void);
59 int main(argc, argv)
60 int argc;
61 char **argv;
63 #ifdef perprintf
64 char errbuf[BUFSIZ];
65 setbuf(stderr, errbuf);
66 perprintf(stderr);
67 #endif
69 while (++argv, --argc) {
70 if ('-' == (*argv)[0]) {
71 if ('u' == (*argv)[1])
72 ++unlocked;
73 else if ('l' == (*argv)[1])
74 ++relock;
75 else {
76 fprintf(stderr, "ci: illegal option -%c\n", (*argv)[1]);
77 exit(1);
79 } else
80 break;
83 if (1 != argc) {
84 fprintf(stderr, "ci: bad number of files arguments\n");
85 exit(1);
87 fname(*argv, file);
88 svcname(file, svc);
90 fprintf(stderr, "%s -> %s\n", file, svc);
92 signal(SIGHUP, onintr);
93 signal(SIGINT, onintr);
94 signal(SIGTERM, onintr);
96 #ifndef BSD
97 if (NULL == (p = strrchr(file, '/')))
98 p = file;
99 else
100 ++p;
102 if (strlen(p) > 13) {
103 fprintf(stderr, "ci: filename %s is too long\n", p);
104 exit(1);
106 #endif /* !BSD */
108 strcpy(newsvc, svc);
109 *(strrchr(newsvc, ',')) = ';'; /* temporary file will be "file;S" */
111 if (NULL == (newfp = fopen(newsvc, "w"))) {
112 perror("ci: can't create SVC temporary");
113 exit(1);
115 (void) mktemp(original);
116 (void) mktemp(diffout);
118 if (NULL != (svcfp = fopen(svc, "r"))) { /* does svc-file exist? */
119 fgets(line, LINELEN, svcfp);
120 if (1 != sscanf(line, "# %d", &rev)) {
121 fprintf(stderr, "ci: %s: illegal SVC file header\n", svc);
122 exit(1);
124 ++rev;
126 if (!lockcheck(svcfp, rev)) {
127 fprintf(stderr, "Revision %d not locked\n", rev);
128 clean();
129 exit(1);
131 if (NULL == (origfp = fopen(original, "w"))) {
132 fprintf(stderr, "ci: can't create %s", original);
133 perror(" ");
135 fgets(line, LINELEN, svcfp); /* skip "cat <<***MAIN-eof***" line */
137 while (NULL != fgets(line, LINELEN, svcfp)
138 && strcmp(line, "***MAIN-eof***\n")) {
139 fputs(line, origfp);
140 if (ferror(origfp)) {
141 perror("ci: origfile");
142 exit(1);
145 fclose(origfp);
147 rundiff();
149 if (0 != stat(original, &stb1) || 0 != stat(diffout, &stb2)) {
150 perror("ci: can't stat original or diffout");
151 clean();
152 exit(1);
154 } else { /* no - create one */
155 rev = 1;
158 fprintf(newfp, "# %d\n", rev);
159 fprintf(newfp, "cat <<***MAIN-eof*** >$1\n");
160 if (NULL == (srcfp = fopen(file, "r"))) {
161 perror("ci: can't read source file");
162 clean();
163 exit(1);
165 while (NULL != fgets(line, LINELEN, srcfp)) fputs(line, newfp);
166 fclose(srcfp);
167 fputs("***MAIN-eof***\n", newfp);
169 if (rev > 1) {
170 fprintf(newfp, "if test $2 -ge %d ; then rm -f Fix.$1 ; exit 0 ; fi ; cat <<***%d-eof*** >Fix.$1\n", rev, rev);
171 p = (stb1.st_size <= stb2.st_size) ? original : diffout;
172 if (NULL == (origfp = fopen(p, "r"))) {
173 perror("can't open diff output file");
174 clean();
175 exit(1);
177 while (NULL != fgets(line, LINELEN, origfp)) fputs(line, newfp);
178 fclose(origfp);
179 fprintf(newfp, "***%d-eof***\n", rev);
180 fputs((original == p) ? "mv Fix.$1 $1\n" : FIX, newfp);
181 logmsg(newfp);
182 while (NULL != fgets(line, LINELEN, svcfp) && strncmp(line, "#***SVCLOCK***", (size_t)14))
183 fputs(line, newfp);
184 } else {
185 logmsg(newfp);
186 fputs("rm -f Fix.$1\n", newfp);
189 if (relock) {
190 fprintf(stderr, "(relocking into revision %d)\n", rev + 1);
191 fprintf(newfp, "#***SVCLOCK*** %s %d\n", whoami(), rev + 1);
193 signal(SIGHUP, SIG_IGN); /* disable during critical section */
194 signal(SIGINT, SIG_IGN);
196 if (ferror(newfp) || fclose(newfp) || ((rev > 1) && unlink(svc))
197 || link(newsvc, svc)) {
198 fprintf(stderr, "SVC file write/link error - Checkin aborted\n");
199 clean();
200 exit(1);
201 } else
202 fprintf(stderr, "Checkin complete.\n");
204 if (stat(svc, &stb1) < 0 || chmod(svc, stb1.st_mode & 0555) < 0)
205 perror("ci: can't chmod SVC file");
207 if (unlocked) {
208 if (stat(file, &stb1) < 0 || chmod(file, stb1.st_mode & 0555) < 0)
209 perror("ci: can't chmod source file");
210 } else if (relock) {
211 if (stat(file, &stb1) < 0 || chmod(file, stb1.st_mode | 0200) < 0)
212 perror("ci: can't chmod source file");
213 } else
214 unlink(file);
216 clean();
217 return(0);
220 void rundiff()
221 { /* do "diff file original > diffout" */
222 int fd; /* redirected output file */
224 switch (fork()) {
225 case -1:
226 perror("ci: fork"); /* error */
227 clean();
228 exit(1);
230 case 0: /* child */
231 if ((fd = creat(diffout, 0600)) < 0 || -1 == dup2(fd, 1)) {
232 perror("ci: diffout");
233 clean();
234 exit(1);
236 close(fd);
237 execlp("diff", "diff", file, original, (char *) 0);
238 perror("ci: exec diff failed");
239 exit(1);
241 default: break; /* parent */
243 wait(&status);
244 if (0 != status && 1 << 8 != status) {
245 fprintf(stderr, "ci: bad return status (0x%x) from diff\n", status);
246 clean();
247 exit(1);
251 void logmsg(fp)
252 FILE *fp;
254 long now;
256 time(&now);
257 fprintf(stderr, "Enter log message for revision %d (end with ^D or '.'):\n", rev);
258 fprintf(fp, "#***SVC*** revision %d %s %s", rev, file, ctime(&now));
259 while (NULL != gets(line) && strcmp(line, "."))
260 fprintf(fp, "#***SVC*** %s\n", line);
263 void fname(src, dst)
264 char *src, *dst;
266 char *p;
267 strcpy(dst, src);
268 p = &dst[strlen(src) - strlen(SUFFIX)];
269 if (!strcmp(p, SUFFIX)) *p = '\0';
272 void svcname(src, dst)
273 char *src, *dst;
275 char *p;
277 strcpy(dst, src);
278 strcat(dst, SUFFIX);
280 if (0 != access(dst, 4)) {
281 char dirname[PATHLEN];
282 if (NULL != (p = strrchr(src, '/')))
283 strncpy(dirname, src, (size_t)(p - src + 1));
284 else
285 dirname[0] = '\0';
286 strcat(dirname, SVCDIR);
288 if (0 == access(dirname, 1)) {
289 strcpy(dst, dirname);
290 if (NULL == p) {
291 strcat(dst, "/");
292 strcat(dst, src);
293 } else
294 strcat(dst, p);
295 strcat(dst, SUFFIX);
300 int lockcheck(fp, rev)
301 FILE *fp;
302 int rev;
304 char lock[40], check[40];
305 long pos;
306 int ret;
308 sprintf(lock, "#***SVCLOCK*** %s %d\n", whoami(), rev);
310 pos = ftell(fp);
311 fseek(fp, -((long) strlen(lock)), 2);
312 fgets(check, 40, fp);
313 ret = (0 == strcmp(lock, check));
314 fseek(fp, pos, 0);
316 return ret;
319 void onintr(dummy)
320 int dummy; /* to keep the compiler happy */
322 fprintf(stderr, "Interrupt - Aborting checkin, cleaning up\n");
323 clean();
324 exit(1);
327 void clean()
329 if (strlen(original)) /* if only more programs made this check! */
330 unlink(original);
331 if (strlen(diffout)) unlink(diffout);
332 if (strlen(newsvc)) unlink(newsvc);
335 char *whoami()
337 struct passwd *pw;
339 if (NULL != (pw = getpwuid(getuid())))
340 return pw->pw_name;
341 else
342 return "nobody";