Blame


1 d947271f 2019-02-08 stsp /* $OpenBSD: rcsutil.c,v 1.46 2017/08/29 16:47:33 otto Exp $ */
2 d947271f 2019-02-08 stsp /*
3 d947271f 2019-02-08 stsp * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org>
4 d947271f 2019-02-08 stsp * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
5 d947271f 2019-02-08 stsp * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
6 d947271f 2019-02-08 stsp * Copyright (c) 2006 Ray Lai <ray@openbsd.org>
7 d947271f 2019-02-08 stsp * All rights reserved.
8 d947271f 2019-02-08 stsp *
9 d947271f 2019-02-08 stsp * Redistribution and use in source and binary forms, with or without
10 d947271f 2019-02-08 stsp * modification, are permitted provided that the following conditions
11 d947271f 2019-02-08 stsp * are met:
12 d947271f 2019-02-08 stsp *
13 d947271f 2019-02-08 stsp * 1. Redistributions of source code must retain the above copyright
14 d947271f 2019-02-08 stsp * notice, this list of conditions and the following disclaimer.
15 d947271f 2019-02-08 stsp * 2. The name of the author may not be used to endorse or promote products
16 d947271f 2019-02-08 stsp * derived from this software without specific prior written permission.
17 d947271f 2019-02-08 stsp *
18 d947271f 2019-02-08 stsp * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 d947271f 2019-02-08 stsp * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 d947271f 2019-02-08 stsp * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 d947271f 2019-02-08 stsp * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 d947271f 2019-02-08 stsp * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 d947271f 2019-02-08 stsp * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 d947271f 2019-02-08 stsp * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 d947271f 2019-02-08 stsp * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 d947271f 2019-02-08 stsp * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 d947271f 2019-02-08 stsp * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 d947271f 2019-02-08 stsp */
29 d947271f 2019-02-08 stsp
30 d947271f 2019-02-08 stsp #include <sys/stat.h>
31 d947271f 2019-02-08 stsp #include <sys/time.h>
32 d947271f 2019-02-08 stsp
33 d947271f 2019-02-08 stsp #include <ctype.h>
34 d947271f 2019-02-08 stsp #include <err.h>
35 d947271f 2019-02-08 stsp #include <fcntl.h>
36 d947271f 2019-02-08 stsp #include <stdio.h>
37 d947271f 2019-02-08 stsp #include <stdlib.h>
38 d947271f 2019-02-08 stsp #include <string.h>
39 d947271f 2019-02-08 stsp #include <unistd.h>
40 d947271f 2019-02-08 stsp
41 d947271f 2019-02-08 stsp #include "rcsprog.h"
42 d947271f 2019-02-08 stsp
43 d947271f 2019-02-08 stsp /*
44 d947271f 2019-02-08 stsp * rcs_get_mtime()
45 d947271f 2019-02-08 stsp *
46 d947271f 2019-02-08 stsp * Get <filename> last modified time.
47 d947271f 2019-02-08 stsp * Returns last modified time on success, or -1 on failure.
48 d947271f 2019-02-08 stsp */
49 d947271f 2019-02-08 stsp time_t
50 d947271f 2019-02-08 stsp rcs_get_mtime(RCSFILE *file)
51 d947271f 2019-02-08 stsp {
52 d947271f 2019-02-08 stsp struct stat st;
53 d947271f 2019-02-08 stsp time_t mtime;
54 d947271f 2019-02-08 stsp
55 d947271f 2019-02-08 stsp if (file->rf_file == NULL)
56 d947271f 2019-02-08 stsp return (-1);
57 d947271f 2019-02-08 stsp
58 d947271f 2019-02-08 stsp if (fstat(fileno(file->rf_file), &st) == -1) {
59 d947271f 2019-02-08 stsp warn("%s", file->rf_path);
60 d947271f 2019-02-08 stsp return (-1);
61 d947271f 2019-02-08 stsp }
62 d947271f 2019-02-08 stsp
63 d947271f 2019-02-08 stsp mtime = st.st_mtimespec.tv_sec;
64 d947271f 2019-02-08 stsp
65 d947271f 2019-02-08 stsp return (mtime);
66 d947271f 2019-02-08 stsp }
67 d947271f 2019-02-08 stsp
68 d947271f 2019-02-08 stsp /*
69 d947271f 2019-02-08 stsp * rcs_set_mtime()
70 d947271f 2019-02-08 stsp *
71 d947271f 2019-02-08 stsp * Set <filename> last modified time to <mtime> if it's not set to -1.
72 d947271f 2019-02-08 stsp */
73 d947271f 2019-02-08 stsp void
74 d947271f 2019-02-08 stsp rcs_set_mtime(RCSFILE *file, time_t mtime)
75 d947271f 2019-02-08 stsp {
76 d947271f 2019-02-08 stsp static struct timeval tv[2];
77 d947271f 2019-02-08 stsp
78 d947271f 2019-02-08 stsp if (file->rf_file == NULL || mtime == -1)
79 d947271f 2019-02-08 stsp return;
80 d947271f 2019-02-08 stsp
81 d947271f 2019-02-08 stsp tv[0].tv_sec = mtime;
82 d947271f 2019-02-08 stsp tv[1].tv_sec = tv[0].tv_sec;
83 d947271f 2019-02-08 stsp
84 d947271f 2019-02-08 stsp if (futimes(fileno(file->rf_file), tv) == -1)
85 d947271f 2019-02-08 stsp err(1, "utimes");
86 d947271f 2019-02-08 stsp }
87 d947271f 2019-02-08 stsp
88 d947271f 2019-02-08 stsp int
89 d947271f 2019-02-08 stsp rcs_getopt(int argc, char **argv, const char *optstr)
90 d947271f 2019-02-08 stsp {
91 d947271f 2019-02-08 stsp char *a;
92 d947271f 2019-02-08 stsp const char *c;
93 d947271f 2019-02-08 stsp static int i = 1;
94 d947271f 2019-02-08 stsp int opt, hasargument, ret;
95 d947271f 2019-02-08 stsp
96 d947271f 2019-02-08 stsp hasargument = 0;
97 d947271f 2019-02-08 stsp rcs_optarg = NULL;
98 d947271f 2019-02-08 stsp
99 d947271f 2019-02-08 stsp if (i >= argc)
100 d947271f 2019-02-08 stsp return (-1);
101 d947271f 2019-02-08 stsp
102 d947271f 2019-02-08 stsp a = argv[i++];
103 d947271f 2019-02-08 stsp if (*a++ != '-')
104 d947271f 2019-02-08 stsp return (-1);
105 d947271f 2019-02-08 stsp
106 d947271f 2019-02-08 stsp ret = 0;
107 d947271f 2019-02-08 stsp opt = *a;
108 d947271f 2019-02-08 stsp for (c = optstr; *c != '\0'; c++) {
109 d947271f 2019-02-08 stsp if (*c == opt) {
110 d947271f 2019-02-08 stsp a++;
111 d947271f 2019-02-08 stsp ret = opt;
112 d947271f 2019-02-08 stsp
113 d947271f 2019-02-08 stsp if (*(c + 1) == ':') {
114 d947271f 2019-02-08 stsp if (*(c + 2) == ':') {
115 d947271f 2019-02-08 stsp if (*a != '\0')
116 d947271f 2019-02-08 stsp hasargument = 1;
117 d947271f 2019-02-08 stsp } else {
118 d947271f 2019-02-08 stsp if (*a != '\0') {
119 d947271f 2019-02-08 stsp hasargument = 1;
120 d947271f 2019-02-08 stsp } else {
121 d947271f 2019-02-08 stsp ret = 1;
122 d947271f 2019-02-08 stsp break;
123 d947271f 2019-02-08 stsp }
124 d947271f 2019-02-08 stsp }
125 d947271f 2019-02-08 stsp }
126 d947271f 2019-02-08 stsp
127 d947271f 2019-02-08 stsp if (hasargument == 1)
128 d947271f 2019-02-08 stsp rcs_optarg = a;
129 d947271f 2019-02-08 stsp
130 d947271f 2019-02-08 stsp if (ret == opt)
131 d947271f 2019-02-08 stsp rcs_optind++;
132 d947271f 2019-02-08 stsp break;
133 d947271f 2019-02-08 stsp }
134 d947271f 2019-02-08 stsp }
135 d947271f 2019-02-08 stsp
136 d947271f 2019-02-08 stsp if (ret == 0)
137 d947271f 2019-02-08 stsp warnx("unknown option -%c", opt);
138 d947271f 2019-02-08 stsp else if (ret == 1)
139 d947271f 2019-02-08 stsp warnx("missing argument for option -%c", opt);
140 d947271f 2019-02-08 stsp
141 d947271f 2019-02-08 stsp return (ret);
142 d947271f 2019-02-08 stsp }
143 d947271f 2019-02-08 stsp
144 d947271f 2019-02-08 stsp /*
145 d947271f 2019-02-08 stsp * rcs_choosefile()
146 d947271f 2019-02-08 stsp *
147 d947271f 2019-02-08 stsp * Given a relative filename, decide where the corresponding RCS file
148 d947271f 2019-02-08 stsp * should be. Tries each extension until a file is found. If no file
149 d947271f 2019-02-08 stsp * was found, returns a path with the first extension.
150 d947271f 2019-02-08 stsp *
151 d947271f 2019-02-08 stsp * Opens and returns file descriptor to RCS file.
152 d947271f 2019-02-08 stsp */
153 d947271f 2019-02-08 stsp int
154 d947271f 2019-02-08 stsp rcs_choosefile(const char *filename, char *out, size_t len)
155 d947271f 2019-02-08 stsp {
156 d947271f 2019-02-08 stsp int fd;
157 d947271f 2019-02-08 stsp struct stat sb;
158 d947271f 2019-02-08 stsp char *p, *ext, name[PATH_MAX], *next, *ptr, rcsdir[PATH_MAX],
159 d947271f 2019-02-08 stsp *suffixes, rcspath[PATH_MAX];
160 d947271f 2019-02-08 stsp
161 d947271f 2019-02-08 stsp /*
162 d947271f 2019-02-08 stsp * If `filename' contains a directory, `rcspath' contains that
163 d947271f 2019-02-08 stsp * directory, including a trailing slash. Otherwise `rcspath'
164 d947271f 2019-02-08 stsp * contains an empty string.
165 d947271f 2019-02-08 stsp */
166 d947271f 2019-02-08 stsp if (strlcpy(rcspath, filename, sizeof(rcspath)) >= sizeof(rcspath))
167 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
168 d947271f 2019-02-08 stsp
169 d947271f 2019-02-08 stsp /* If `/' is found, end string after `/'. */
170 d947271f 2019-02-08 stsp if ((ptr = strrchr(rcspath, '/')) != NULL)
171 d947271f 2019-02-08 stsp *(++ptr) = '\0';
172 d947271f 2019-02-08 stsp else
173 d947271f 2019-02-08 stsp rcspath[0] = '\0';
174 d947271f 2019-02-08 stsp
175 d947271f 2019-02-08 stsp /* Append RCS/ to `rcspath' if it exists. */
176 d947271f 2019-02-08 stsp if (strlcpy(rcsdir, rcspath, sizeof(rcsdir)) >= sizeof(rcsdir) ||
177 d947271f 2019-02-08 stsp strlcat(rcsdir, RCSDIR, sizeof(rcsdir)) >= sizeof(rcsdir))
178 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
179 d947271f 2019-02-08 stsp
180 d947271f 2019-02-08 stsp if (stat(rcsdir, &sb) == 0 && S_ISDIR(sb.st_mode))
181 d947271f 2019-02-08 stsp if (strlcpy(rcspath, rcsdir, sizeof(rcspath))
182 d947271f 2019-02-08 stsp >= sizeof(rcspath) ||
183 d947271f 2019-02-08 stsp strlcat(rcspath, "/", sizeof(rcspath)) >= sizeof(rcspath))
184 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
185 d947271f 2019-02-08 stsp
186 d947271f 2019-02-08 stsp /* Name of file without path. */
187 d947271f 2019-02-08 stsp if ((ptr = strrchr(filename, '/')) == NULL) {
188 d947271f 2019-02-08 stsp if (strlcpy(name, filename, sizeof(name)) >= sizeof(name))
189 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
190 d947271f 2019-02-08 stsp } else {
191 d947271f 2019-02-08 stsp /* Skip `/'. */
192 d947271f 2019-02-08 stsp if (strlcpy(name, ptr + 1, sizeof(name)) >= sizeof(name))
193 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
194 d947271f 2019-02-08 stsp }
195 d947271f 2019-02-08 stsp
196 d947271f 2019-02-08 stsp /* Name of RCS file without an extension. */
197 d947271f 2019-02-08 stsp if (strlcat(rcspath, name, sizeof(rcspath)) >= sizeof(rcspath))
198 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
199 d947271f 2019-02-08 stsp
200 d947271f 2019-02-08 stsp /*
201 d947271f 2019-02-08 stsp * If only the empty suffix was given, use existing rcspath.
202 d947271f 2019-02-08 stsp * This ensures that there is at least one suffix for strsep().
203 d947271f 2019-02-08 stsp */
204 d947271f 2019-02-08 stsp if (strcmp(rcs_suffixes, "") == 0) {
205 d947271f 2019-02-08 stsp if (strlcpy(out, rcspath, len) >= len)
206 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
207 d947271f 2019-02-08 stsp fd = open(rcspath, O_RDONLY);
208 d947271f 2019-02-08 stsp return (fd);
209 d947271f 2019-02-08 stsp }
210 d947271f 2019-02-08 stsp
211 d947271f 2019-02-08 stsp /*
212 d947271f 2019-02-08 stsp * Cycle through slash-separated `rcs_suffixes', appending each
213 d947271f 2019-02-08 stsp * extension to `rcspath' and testing if the file exists. If it
214 d947271f 2019-02-08 stsp * does, return that string. Otherwise return path with first
215 d947271f 2019-02-08 stsp * extension.
216 d947271f 2019-02-08 stsp */
217 d947271f 2019-02-08 stsp suffixes = xstrdup(rcs_suffixes);
218 d947271f 2019-02-08 stsp for (next = suffixes; (ext = strsep(&next, "/")) != NULL;) {
219 d947271f 2019-02-08 stsp char fpath[PATH_MAX];
220 d947271f 2019-02-08 stsp
221 d947271f 2019-02-08 stsp if ((p = strrchr(rcspath, ',')) != NULL) {
222 d947271f 2019-02-08 stsp if (!strcmp(p, ext)) {
223 d947271f 2019-02-08 stsp if ((fd = open(rcspath, O_RDONLY)) == -1)
224 d947271f 2019-02-08 stsp continue;
225 d947271f 2019-02-08 stsp
226 d947271f 2019-02-08 stsp if (fstat(fd, &sb) == -1)
227 d947271f 2019-02-08 stsp err(1, "%s", rcspath);
228 d947271f 2019-02-08 stsp
229 d947271f 2019-02-08 stsp if (strlcpy(out, rcspath, len) >= len)
230 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile; truncation");
231 d947271f 2019-02-08 stsp
232 d947271f 2019-02-08 stsp free(suffixes);
233 d947271f 2019-02-08 stsp return (fd);
234 d947271f 2019-02-08 stsp }
235 d947271f 2019-02-08 stsp
236 d947271f 2019-02-08 stsp continue;
237 d947271f 2019-02-08 stsp }
238 d947271f 2019-02-08 stsp
239 d947271f 2019-02-08 stsp /* Construct RCS file path. */
240 d947271f 2019-02-08 stsp if (strlcpy(fpath, rcspath, sizeof(fpath)) >= sizeof(fpath) ||
241 d947271f 2019-02-08 stsp strlcat(fpath, ext, sizeof(fpath)) >= sizeof(fpath))
242 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
243 d947271f 2019-02-08 stsp
244 d947271f 2019-02-08 stsp /* Don't use `filename' as RCS file. */
245 d947271f 2019-02-08 stsp if (strcmp(fpath, filename) == 0)
246 d947271f 2019-02-08 stsp continue;
247 d947271f 2019-02-08 stsp
248 d947271f 2019-02-08 stsp if ((fd = open(fpath, O_RDONLY)) == -1)
249 d947271f 2019-02-08 stsp continue;
250 d947271f 2019-02-08 stsp
251 d947271f 2019-02-08 stsp if (fstat(fd, &sb) == -1)
252 d947271f 2019-02-08 stsp err(1, "%s", fpath);
253 d947271f 2019-02-08 stsp
254 d947271f 2019-02-08 stsp if (strlcpy(out, fpath, len) >= len)
255 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
256 d947271f 2019-02-08 stsp
257 d947271f 2019-02-08 stsp free(suffixes);
258 d947271f 2019-02-08 stsp return (fd);
259 d947271f 2019-02-08 stsp }
260 d947271f 2019-02-08 stsp
261 d947271f 2019-02-08 stsp /*
262 d947271f 2019-02-08 stsp * `suffixes' should now be NUL separated, so the first
263 d947271f 2019-02-08 stsp * extension can be read just by reading `suffixes'.
264 d947271f 2019-02-08 stsp */
265 d947271f 2019-02-08 stsp if (strlcat(rcspath, suffixes, sizeof(rcspath)) >= sizeof(rcspath))
266 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
267 d947271f 2019-02-08 stsp
268 d947271f 2019-02-08 stsp free(suffixes);
269 d947271f 2019-02-08 stsp
270 d947271f 2019-02-08 stsp if (strlcpy(out, rcspath, len) >= len)
271 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
272 d947271f 2019-02-08 stsp
273 d947271f 2019-02-08 stsp fd = open(rcspath, O_RDONLY);
274 d947271f 2019-02-08 stsp
275 d947271f 2019-02-08 stsp return (fd);
276 d947271f 2019-02-08 stsp }
277 d947271f 2019-02-08 stsp
278 d947271f 2019-02-08 stsp /*
279 d947271f 2019-02-08 stsp * Set <str> to <new_str>. Print warning if <str> is redefined.
280 d947271f 2019-02-08 stsp */
281 d947271f 2019-02-08 stsp void
282 d947271f 2019-02-08 stsp rcs_setrevstr(char **str, char *new_str)
283 d947271f 2019-02-08 stsp {
284 d947271f 2019-02-08 stsp if (new_str == NULL)
285 d947271f 2019-02-08 stsp return;
286 d947271f 2019-02-08 stsp if (*str != NULL)
287 d947271f 2019-02-08 stsp warnx("redefinition of revision number");
288 d947271f 2019-02-08 stsp *str = new_str;
289 d947271f 2019-02-08 stsp }
290 d947271f 2019-02-08 stsp
291 d947271f 2019-02-08 stsp /*
292 d947271f 2019-02-08 stsp * Set <str1> or <str2> to <new_str>, depending on which is not set.
293 d947271f 2019-02-08 stsp * If both are set, error out.
294 d947271f 2019-02-08 stsp */
295 d947271f 2019-02-08 stsp void
296 d947271f 2019-02-08 stsp rcs_setrevstr2(char **str1, char **str2, char *new_str)
297 d947271f 2019-02-08 stsp {
298 d947271f 2019-02-08 stsp if (new_str == NULL)
299 d947271f 2019-02-08 stsp return;
300 d947271f 2019-02-08 stsp if (*str1 == NULL)
301 d947271f 2019-02-08 stsp *str1 = new_str;
302 d947271f 2019-02-08 stsp else if (*str2 == NULL)
303 d947271f 2019-02-08 stsp *str2 = new_str;
304 d947271f 2019-02-08 stsp else
305 d947271f 2019-02-08 stsp errx(1, "too many revision numbers");
306 d947271f 2019-02-08 stsp }
307 d947271f 2019-02-08 stsp
308 d947271f 2019-02-08 stsp /*
309 d947271f 2019-02-08 stsp * Get revision from file. The revision can be specified as a symbol or
310 d947271f 2019-02-08 stsp * a revision number.
311 d947271f 2019-02-08 stsp */
312 d947271f 2019-02-08 stsp RCSNUM *
313 d947271f 2019-02-08 stsp rcs_getrevnum(const char *rev_str, RCSFILE *file)
314 d947271f 2019-02-08 stsp {
315 d947271f 2019-02-08 stsp RCSNUM *rev;
316 d947271f 2019-02-08 stsp
317 d947271f 2019-02-08 stsp /* Search for symbol. */
318 d947271f 2019-02-08 stsp rev = rcs_sym_getrev(file, rev_str);
319 d947271f 2019-02-08 stsp
320 d947271f 2019-02-08 stsp /* Search for revision number. */
321 d947271f 2019-02-08 stsp if (rev == NULL)
322 d947271f 2019-02-08 stsp rev = rcsnum_parse(rev_str);
323 d947271f 2019-02-08 stsp
324 d947271f 2019-02-08 stsp return (rev);
325 d947271f 2019-02-08 stsp }
326 d947271f 2019-02-08 stsp
327 d947271f 2019-02-08 stsp /*
328 d947271f 2019-02-08 stsp * Prompt for and store user's input in an allocated string.
329 d947271f 2019-02-08 stsp *
330 d947271f 2019-02-08 stsp * Returns the string's pointer.
331 d947271f 2019-02-08 stsp */
332 d947271f 2019-02-08 stsp char *
333 d947271f 2019-02-08 stsp rcs_prompt(const char *prompt, int flags)
334 d947271f 2019-02-08 stsp {
335 d947271f 2019-02-08 stsp BUF *bp;
336 d947271f 2019-02-08 stsp size_t len;
337 d947271f 2019-02-08 stsp char *buf;
338 d947271f 2019-02-08 stsp
339 d947271f 2019-02-08 stsp if (!(flags & INTERACTIVE) && isatty(STDIN_FILENO))
340 d947271f 2019-02-08 stsp flags |= INTERACTIVE;
341 d947271f 2019-02-08 stsp
342 d947271f 2019-02-08 stsp bp = buf_alloc(0);
343 d947271f 2019-02-08 stsp if (flags & INTERACTIVE)
344 d947271f 2019-02-08 stsp (void)fprintf(stderr, "%s", prompt);
345 d947271f 2019-02-08 stsp if (flags & INTERACTIVE)
346 d947271f 2019-02-08 stsp (void)fprintf(stderr, ">> ");
347 d947271f 2019-02-08 stsp clearerr(stdin);
348 d947271f 2019-02-08 stsp while ((buf = fgetln(stdin, &len)) != NULL) {
349 d947271f 2019-02-08 stsp /* The last line may not be EOL terminated. */
350 d947271f 2019-02-08 stsp if (buf[0] == '.' && (len == 1 || buf[1] == '\n'))
351 d947271f 2019-02-08 stsp break;
352 d947271f 2019-02-08 stsp else
353 d947271f 2019-02-08 stsp buf_append(bp, buf, len);
354 d947271f 2019-02-08 stsp
355 d947271f 2019-02-08 stsp if (flags & INTERACTIVE)
356 d947271f 2019-02-08 stsp (void)fprintf(stderr, ">> ");
357 d947271f 2019-02-08 stsp }
358 d947271f 2019-02-08 stsp buf_putc(bp, '\0');
359 d947271f 2019-02-08 stsp
360 d947271f 2019-02-08 stsp return (buf_release(bp));
361 d947271f 2019-02-08 stsp }
362 d947271f 2019-02-08 stsp
363 d947271f 2019-02-08 stsp u_int
364 d947271f 2019-02-08 stsp rcs_rev_select(RCSFILE *file, const char *range)
365 d947271f 2019-02-08 stsp {
366 d947271f 2019-02-08 stsp int i;
367 d947271f 2019-02-08 stsp u_int nrev;
368 d947271f 2019-02-08 stsp const char *ep;
369 d947271f 2019-02-08 stsp char *lstr, *rstr;
370 d947271f 2019-02-08 stsp struct rcs_delta *rdp;
371 d947271f 2019-02-08 stsp struct rcs_argvector *revargv, *revrange;
372 d947271f 2019-02-08 stsp RCSNUM lnum, rnum;
373 d947271f 2019-02-08 stsp
374 d947271f 2019-02-08 stsp nrev = 0;
375 d947271f 2019-02-08 stsp (void)memset(&lnum, 0, sizeof(lnum));
376 d947271f 2019-02-08 stsp (void)memset(&rnum, 0, sizeof(rnum));
377 d947271f 2019-02-08 stsp
378 d947271f 2019-02-08 stsp if (range == NULL) {
379 d947271f 2019-02-08 stsp TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
380 d947271f 2019-02-08 stsp if (rcsnum_cmp(rdp->rd_num, file->rf_head, 0) == 0) {
381 d947271f 2019-02-08 stsp rdp->rd_flags |= RCS_RD_SELECT;
382 d947271f 2019-02-08 stsp return (1);
383 d947271f 2019-02-08 stsp }
384 d947271f 2019-02-08 stsp return (0);
385 d947271f 2019-02-08 stsp }
386 d947271f 2019-02-08 stsp
387 d947271f 2019-02-08 stsp revargv = rcs_strsplit(range, ",");
388 d947271f 2019-02-08 stsp for (i = 0; revargv->argv[i] != NULL; i++) {
389 d947271f 2019-02-08 stsp revrange = rcs_strsplit(revargv->argv[i], ":");
390 d947271f 2019-02-08 stsp if (revrange->argv[0] == NULL)
391 d947271f 2019-02-08 stsp /* should not happen */
392 d947271f 2019-02-08 stsp errx(1, "invalid revision range: %s", revargv->argv[i]);
393 d947271f 2019-02-08 stsp else if (revrange->argv[1] == NULL)
394 d947271f 2019-02-08 stsp lstr = rstr = revrange->argv[0];
395 d947271f 2019-02-08 stsp else {
396 d947271f 2019-02-08 stsp if (revrange->argv[2] != NULL)
397 d947271f 2019-02-08 stsp errx(1, "invalid revision range: %s",
398 d947271f 2019-02-08 stsp revargv->argv[i]);
399 d947271f 2019-02-08 stsp lstr = revrange->argv[0];
400 d947271f 2019-02-08 stsp rstr = revrange->argv[1];
401 d947271f 2019-02-08 stsp if (strcmp(lstr, "") == 0)
402 d947271f 2019-02-08 stsp lstr = NULL;
403 d947271f 2019-02-08 stsp if (strcmp(rstr, "") == 0)
404 d947271f 2019-02-08 stsp rstr = NULL;
405 d947271f 2019-02-08 stsp }
406 d947271f 2019-02-08 stsp
407 d947271f 2019-02-08 stsp if (lstr == NULL)
408 d947271f 2019-02-08 stsp lstr = RCS_HEAD_INIT;
409 d947271f 2019-02-08 stsp if (rcsnum_aton(lstr, &ep, &lnum) == 0 || (*ep != '\0'))
410 d947271f 2019-02-08 stsp errx(1, "invalid revision: %s", lstr);
411 d947271f 2019-02-08 stsp
412 d947271f 2019-02-08 stsp if (rstr != NULL) {
413 d947271f 2019-02-08 stsp if (rcsnum_aton(rstr, &ep, &rnum) == 0 || (*ep != '\0'))
414 d947271f 2019-02-08 stsp errx(1, "invalid revision: %s", rstr);
415 d947271f 2019-02-08 stsp } else
416 d947271f 2019-02-08 stsp rcsnum_cpy(file->rf_head, &rnum, 0);
417 d947271f 2019-02-08 stsp
418 d947271f 2019-02-08 stsp rcs_argv_destroy(revrange);
419 d947271f 2019-02-08 stsp
420 d947271f 2019-02-08 stsp TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
421 d947271f 2019-02-08 stsp if (rcsnum_cmp(rdp->rd_num, &lnum, 0) <= 0 &&
422 d947271f 2019-02-08 stsp rcsnum_cmp(rdp->rd_num, &rnum, 0) >= 0 &&
423 d947271f 2019-02-08 stsp !(rdp->rd_flags & RCS_RD_SELECT)) {
424 d947271f 2019-02-08 stsp rdp->rd_flags |= RCS_RD_SELECT;
425 d947271f 2019-02-08 stsp nrev++;
426 d947271f 2019-02-08 stsp }
427 d947271f 2019-02-08 stsp }
428 d947271f 2019-02-08 stsp rcs_argv_destroy(revargv);
429 d947271f 2019-02-08 stsp
430 d947271f 2019-02-08 stsp free(lnum.rn_id);
431 d947271f 2019-02-08 stsp free(rnum.rn_id);
432 d947271f 2019-02-08 stsp
433 d947271f 2019-02-08 stsp return (nrev);
434 d947271f 2019-02-08 stsp }
435 d947271f 2019-02-08 stsp
436 d947271f 2019-02-08 stsp /*
437 d947271f 2019-02-08 stsp * Load description from <in> to <file>.
438 d947271f 2019-02-08 stsp * If <in> starts with a `-', <in> is taken as the description.
439 d947271f 2019-02-08 stsp * Otherwise <in> is the name of the file containing the description.
440 d947271f 2019-02-08 stsp * If <in> is NULL, the description is read from stdin.
441 d947271f 2019-02-08 stsp * Returns 0 on success, -1 on failure, setting errno.
442 d947271f 2019-02-08 stsp */
443 d947271f 2019-02-08 stsp int
444 d947271f 2019-02-08 stsp rcs_set_description(RCSFILE *file, const char *in, int flags)
445 d947271f 2019-02-08 stsp {
446 d947271f 2019-02-08 stsp BUF *bp;
447 d947271f 2019-02-08 stsp char *content;
448 d947271f 2019-02-08 stsp const char *prompt =
449 d947271f 2019-02-08 stsp "enter description, terminated with single '.' or end of file:\n"
450 d947271f 2019-02-08 stsp "NOTE: This is NOT the log message!\n";
451 d947271f 2019-02-08 stsp
452 d947271f 2019-02-08 stsp /* Description is in file <in>. */
453 d947271f 2019-02-08 stsp if (in != NULL && *in != '-') {
454 d947271f 2019-02-08 stsp if ((bp = buf_load(in)) == NULL)
455 d947271f 2019-02-08 stsp return (-1);
456 d947271f 2019-02-08 stsp buf_putc(bp, '\0');
457 d947271f 2019-02-08 stsp content = buf_release(bp);
458 d947271f 2019-02-08 stsp /* Description is in <in>. */
459 d947271f 2019-02-08 stsp } else if (in != NULL)
460 d947271f 2019-02-08 stsp /* Skip leading `-'. */
461 d947271f 2019-02-08 stsp content = xstrdup(in + 1);
462 d947271f 2019-02-08 stsp /* Get description from stdin. */
463 d947271f 2019-02-08 stsp else
464 d947271f 2019-02-08 stsp content = rcs_prompt(prompt, flags);
465 d947271f 2019-02-08 stsp
466 d947271f 2019-02-08 stsp rcs_desc_set(file, content);
467 d947271f 2019-02-08 stsp free(content);
468 d947271f 2019-02-08 stsp return (0);
469 d947271f 2019-02-08 stsp }
470 d947271f 2019-02-08 stsp
471 d947271f 2019-02-08 stsp /*
472 d947271f 2019-02-08 stsp * Split the contents of a file into a list of lines.
473 d947271f 2019-02-08 stsp */
474 d947271f 2019-02-08 stsp struct rcs_lines *
475 d947271f 2019-02-08 stsp rcs_splitlines(u_char *data, size_t len)
476 d947271f 2019-02-08 stsp {
477 d947271f 2019-02-08 stsp u_char *c, *p;
478 d947271f 2019-02-08 stsp struct rcs_lines *lines;
479 d947271f 2019-02-08 stsp struct rcs_line *lp;
480 d947271f 2019-02-08 stsp size_t i, tlen;
481 d947271f 2019-02-08 stsp
482 d947271f 2019-02-08 stsp lines = xcalloc(1, sizeof(*lines));
483 d947271f 2019-02-08 stsp TAILQ_INIT(&(lines->l_lines));
484 d947271f 2019-02-08 stsp
485 d947271f 2019-02-08 stsp lp = xcalloc(1, sizeof(*lp));
486 d947271f 2019-02-08 stsp TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
487 d947271f 2019-02-08 stsp
488 d947271f 2019-02-08 stsp
489 d947271f 2019-02-08 stsp p = c = data;
490 d947271f 2019-02-08 stsp for (i = 0; i < len; i++) {
491 d947271f 2019-02-08 stsp if (*p == '\n' || (i == len - 1)) {
492 d947271f 2019-02-08 stsp tlen = p - c + 1;
493 d947271f 2019-02-08 stsp lp = xmalloc(sizeof(*lp));
494 d947271f 2019-02-08 stsp lp->l_line = c;
495 d947271f 2019-02-08 stsp lp->l_len = tlen;
496 d947271f 2019-02-08 stsp lp->l_lineno = ++(lines->l_nblines);
497 d947271f 2019-02-08 stsp TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
498 d947271f 2019-02-08 stsp c = p + 1;
499 d947271f 2019-02-08 stsp }
500 d947271f 2019-02-08 stsp p++;
501 d947271f 2019-02-08 stsp }
502 d947271f 2019-02-08 stsp
503 d947271f 2019-02-08 stsp return (lines);
504 d947271f 2019-02-08 stsp }
505 d947271f 2019-02-08 stsp
506 d947271f 2019-02-08 stsp void
507 d947271f 2019-02-08 stsp rcs_freelines(struct rcs_lines *lines)
508 d947271f 2019-02-08 stsp {
509 d947271f 2019-02-08 stsp struct rcs_line *lp;
510 d947271f 2019-02-08 stsp
511 d947271f 2019-02-08 stsp while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
512 d947271f 2019-02-08 stsp TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
513 d947271f 2019-02-08 stsp free(lp);
514 d947271f 2019-02-08 stsp }
515 d947271f 2019-02-08 stsp
516 d947271f 2019-02-08 stsp free(lines);
517 d947271f 2019-02-08 stsp }
518 d947271f 2019-02-08 stsp
519 d947271f 2019-02-08 stsp BUF *
520 d947271f 2019-02-08 stsp rcs_patchfile(u_char *data, size_t dlen, u_char *patch, size_t plen,
521 d947271f 2019-02-08 stsp int (*p)(struct rcs_lines *, struct rcs_lines *))
522 d947271f 2019-02-08 stsp {
523 d947271f 2019-02-08 stsp struct rcs_lines *dlines, *plines;
524 d947271f 2019-02-08 stsp struct rcs_line *lp;
525 d947271f 2019-02-08 stsp BUF *res;
526 d947271f 2019-02-08 stsp
527 d947271f 2019-02-08 stsp dlines = rcs_splitlines(data, dlen);
528 d947271f 2019-02-08 stsp plines = rcs_splitlines(patch, plen);
529 d947271f 2019-02-08 stsp
530 d947271f 2019-02-08 stsp if (p(dlines, plines) < 0) {
531 d947271f 2019-02-08 stsp rcs_freelines(dlines);
532 d947271f 2019-02-08 stsp rcs_freelines(plines);
533 d947271f 2019-02-08 stsp return (NULL);
534 d947271f 2019-02-08 stsp }
535 d947271f 2019-02-08 stsp
536 d947271f 2019-02-08 stsp res = buf_alloc(1024);
537 d947271f 2019-02-08 stsp TAILQ_FOREACH(lp, &dlines->l_lines, l_list) {
538 d947271f 2019-02-08 stsp if (lp->l_line == NULL)
539 d947271f 2019-02-08 stsp continue;
540 d947271f 2019-02-08 stsp buf_append(res, lp->l_line, lp->l_len);
541 d947271f 2019-02-08 stsp }
542 d947271f 2019-02-08 stsp
543 d947271f 2019-02-08 stsp rcs_freelines(dlines);
544 d947271f 2019-02-08 stsp rcs_freelines(plines);
545 d947271f 2019-02-08 stsp return (res);
546 d947271f 2019-02-08 stsp }
547 d947271f 2019-02-08 stsp
548 d947271f 2019-02-08 stsp /*
549 d947271f 2019-02-08 stsp * rcs_yesno()
550 d947271f 2019-02-08 stsp *
551 d947271f 2019-02-08 stsp * Read a char from standard input, returns defc if the
552 d947271f 2019-02-08 stsp * user enters an equivalent to defc, else whatever char
553 d947271f 2019-02-08 stsp * was entered. Converts input to lower case.
554 d947271f 2019-02-08 stsp */
555 d947271f 2019-02-08 stsp int
556 d947271f 2019-02-08 stsp rcs_yesno(int defc)
557 d947271f 2019-02-08 stsp {
558 d947271f 2019-02-08 stsp int c, ret;
559 d947271f 2019-02-08 stsp
560 d947271f 2019-02-08 stsp fflush(stderr);
561 d947271f 2019-02-08 stsp fflush(stdout);
562 d947271f 2019-02-08 stsp
563 d947271f 2019-02-08 stsp clearerr(stdin);
564 d947271f 2019-02-08 stsp if (isalpha(c = getchar()))
565 d947271f 2019-02-08 stsp c = tolower(c);
566 d947271f 2019-02-08 stsp if (c == defc || c == '\n' || (c == EOF && feof(stdin)))
567 d947271f 2019-02-08 stsp ret = defc;
568 d947271f 2019-02-08 stsp else
569 d947271f 2019-02-08 stsp ret = c;
570 d947271f 2019-02-08 stsp
571 d947271f 2019-02-08 stsp while (c != EOF && c != '\n')
572 d947271f 2019-02-08 stsp c = getchar();
573 d947271f 2019-02-08 stsp
574 d947271f 2019-02-08 stsp return (ret);
575 d947271f 2019-02-08 stsp }
576 d947271f 2019-02-08 stsp
577 d947271f 2019-02-08 stsp /*
578 d947271f 2019-02-08 stsp * rcs_strsplit()
579 d947271f 2019-02-08 stsp *
580 d947271f 2019-02-08 stsp * Split a string <str> of <sep>-separated values and allocate
581 d947271f 2019-02-08 stsp * an argument vector for the values found.
582 d947271f 2019-02-08 stsp */
583 d947271f 2019-02-08 stsp struct rcs_argvector *
584 d947271f 2019-02-08 stsp rcs_strsplit(const char *str, const char *sep)
585 d947271f 2019-02-08 stsp {
586 d947271f 2019-02-08 stsp struct rcs_argvector *av;
587 d947271f 2019-02-08 stsp size_t i = 0;
588 d947271f 2019-02-08 stsp char *cp, *p;
589 d947271f 2019-02-08 stsp
590 d947271f 2019-02-08 stsp cp = xstrdup(str);
591 d947271f 2019-02-08 stsp av = xmalloc(sizeof(*av));
592 d947271f 2019-02-08 stsp av->str = cp;
593 d947271f 2019-02-08 stsp av->argv = xmalloc(sizeof(*(av->argv)));
594 d947271f 2019-02-08 stsp
595 d947271f 2019-02-08 stsp while ((p = strsep(&cp, sep)) != NULL) {
596 d947271f 2019-02-08 stsp av->argv[i++] = p;
597 d947271f 2019-02-08 stsp av->argv = xreallocarray(av->argv,
598 d947271f 2019-02-08 stsp i + 1, sizeof(*(av->argv)));
599 d947271f 2019-02-08 stsp }
600 d947271f 2019-02-08 stsp av->argv[i] = NULL;
601 d947271f 2019-02-08 stsp
602 d947271f 2019-02-08 stsp return (av);
603 d947271f 2019-02-08 stsp }
604 d947271f 2019-02-08 stsp
605 d947271f 2019-02-08 stsp /*
606 d947271f 2019-02-08 stsp * rcs_argv_destroy()
607 d947271f 2019-02-08 stsp *
608 d947271f 2019-02-08 stsp * Free an argument vector previously allocated by rcs_strsplit().
609 d947271f 2019-02-08 stsp */
610 d947271f 2019-02-08 stsp void
611 d947271f 2019-02-08 stsp rcs_argv_destroy(struct rcs_argvector *av)
612 d947271f 2019-02-08 stsp {
613 d947271f 2019-02-08 stsp free(av->str);
614 d947271f 2019-02-08 stsp free(av->argv);
615 d947271f 2019-02-08 stsp free(av);
616 d947271f 2019-02-08 stsp }
617 d947271f 2019-02-08 stsp
618 d947271f 2019-02-08 stsp /*
619 d947271f 2019-02-08 stsp * Strip suffix from filename.
620 d947271f 2019-02-08 stsp */
621 d947271f 2019-02-08 stsp void
622 d947271f 2019-02-08 stsp rcs_strip_suffix(char *filename)
623 d947271f 2019-02-08 stsp {
624 d947271f 2019-02-08 stsp char *p, *suffixes, *next, *ext;
625 d947271f 2019-02-08 stsp
626 d947271f 2019-02-08 stsp if ((p = strrchr(filename, ',')) != NULL) {
627 d947271f 2019-02-08 stsp suffixes = xstrdup(rcs_suffixes);
628 d947271f 2019-02-08 stsp for (next = suffixes; (ext = strsep(&next, "/")) != NULL;) {
629 d947271f 2019-02-08 stsp if (!strcmp(p, ext)) {
630 d947271f 2019-02-08 stsp *p = '\0';
631 d947271f 2019-02-08 stsp break;
632 d947271f 2019-02-08 stsp }
633 d947271f 2019-02-08 stsp }
634 d947271f 2019-02-08 stsp free(suffixes);
635 d947271f 2019-02-08 stsp }
636 d947271f 2019-02-08 stsp }