1 d947271f 2019-02-08 stsp /* $OpenBSD: rcsutil.c,v 1.46 2017/08/29 16:47:33 otto Exp $ */
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.
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
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.
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.
30 d947271f 2019-02-08 stsp #include <sys/stat.h>
31 d947271f 2019-02-08 stsp #include <sys/time.h>
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>
41 d947271f 2019-02-08 stsp #include "rcsprog.h"
44 d947271f 2019-02-08 stsp * rcs_get_mtime()
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.
50 d947271f 2019-02-08 stsp rcs_get_mtime(RCSFILE *file)
52 d947271f 2019-02-08 stsp struct stat st;
53 d947271f 2019-02-08 stsp time_t mtime;
55 d947271f 2019-02-08 stsp if (file->rf_file == NULL)
56 d947271f 2019-02-08 stsp return (-1);
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);
63 d947271f 2019-02-08 stsp mtime = st.st_mtimespec.tv_sec;
65 d947271f 2019-02-08 stsp return (mtime);
69 d947271f 2019-02-08 stsp * rcs_set_mtime()
71 d947271f 2019-02-08 stsp * Set <filename> last modified time to <mtime> if it's not set to -1.
74 d947271f 2019-02-08 stsp rcs_set_mtime(RCSFILE *file, time_t mtime)
76 d947271f 2019-02-08 stsp static struct timeval tv[2];
78 d947271f 2019-02-08 stsp if (file->rf_file == NULL || mtime == -1)
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;
84 d947271f 2019-02-08 stsp if (futimes(fileno(file->rf_file), tv) == -1)
85 d947271f 2019-02-08 stsp err(1, "utimes");
89 d947271f 2019-02-08 stsp rcs_getopt(int argc, char **argv, const char *optstr)
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;
96 d947271f 2019-02-08 stsp hasargument = 0;
97 d947271f 2019-02-08 stsp rcs_optarg = NULL;
99 d947271f 2019-02-08 stsp if (i >= argc)
100 d947271f 2019-02-08 stsp return (-1);
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);
108 d947271f 2019-02-08 stsp for (c = optstr; *c != '\0'; c++) {
109 d947271f 2019-02-08 stsp if (*c == opt) {
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;
118 d947271f 2019-02-08 stsp if (*a != '\0') {
119 d947271f 2019-02-08 stsp hasargument = 1;
127 d947271f 2019-02-08 stsp if (hasargument == 1)
128 d947271f 2019-02-08 stsp rcs_optarg = a;
130 d947271f 2019-02-08 stsp if (ret == opt)
131 d947271f 2019-02-08 stsp rcs_optind++;
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);
141 d947271f 2019-02-08 stsp return (ret);
145 d947271f 2019-02-08 stsp * rcs_choosefile()
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.
151 d947271f 2019-02-08 stsp * Opens and returns file descriptor to RCS file.
154 d947271f 2019-02-08 stsp rcs_choosefile(const char *filename, char *out, size_t len)
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];
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.
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");
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';
173 d947271f 2019-02-08 stsp rcspath[0] = '\0';
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");
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");
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");
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");
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");
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().
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);
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.
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];
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)
226 d947271f 2019-02-08 stsp if (fstat(fd, &sb) == -1)
227 d947271f 2019-02-08 stsp err(1, "%s", rcspath);
229 d947271f 2019-02-08 stsp if (strlcpy(out, rcspath, len) >= len)
230 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile; truncation");
232 d947271f 2019-02-08 stsp free(suffixes);
233 d947271f 2019-02-08 stsp return (fd);
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");
244 d947271f 2019-02-08 stsp /* Don't use `filename' as RCS file. */
245 d947271f 2019-02-08 stsp if (strcmp(fpath, filename) == 0)
248 d947271f 2019-02-08 stsp if ((fd = open(fpath, O_RDONLY)) == -1)
251 d947271f 2019-02-08 stsp if (fstat(fd, &sb) == -1)
252 d947271f 2019-02-08 stsp err(1, "%s", fpath);
254 d947271f 2019-02-08 stsp if (strlcpy(out, fpath, len) >= len)
255 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
257 d947271f 2019-02-08 stsp free(suffixes);
258 d947271f 2019-02-08 stsp return (fd);
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'.
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");
268 d947271f 2019-02-08 stsp free(suffixes);
270 d947271f 2019-02-08 stsp if (strlcpy(out, rcspath, len) >= len)
271 d947271f 2019-02-08 stsp errx(1, "rcs_choosefile: truncation");
273 d947271f 2019-02-08 stsp fd = open(rcspath, O_RDONLY);
275 d947271f 2019-02-08 stsp return (fd);
279 d947271f 2019-02-08 stsp * Set <str> to <new_str>. Print warning if <str> is redefined.
282 d947271f 2019-02-08 stsp rcs_setrevstr(char **str, char *new_str)
284 d947271f 2019-02-08 stsp if (new_str == NULL)
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;
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.
296 d947271f 2019-02-08 stsp rcs_setrevstr2(char **str1, char **str2, char *new_str)
298 d947271f 2019-02-08 stsp if (new_str == NULL)
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;
305 d947271f 2019-02-08 stsp errx(1, "too many revision numbers");
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.
313 d947271f 2019-02-08 stsp rcs_getrevnum(const char *rev_str, RCSFILE *file)
315 d947271f 2019-02-08 stsp RCSNUM *rev;
317 d947271f 2019-02-08 stsp /* Search for symbol. */
318 d947271f 2019-02-08 stsp rev = rcs_sym_getrev(file, rev_str);
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);
324 d947271f 2019-02-08 stsp return (rev);
328 d947271f 2019-02-08 stsp * Prompt for and store user's input in an allocated string.
330 d947271f 2019-02-08 stsp * Returns the string's pointer.
333 d947271f 2019-02-08 stsp rcs_prompt(const char *prompt, int flags)
336 d947271f 2019-02-08 stsp size_t len;
339 d947271f 2019-02-08 stsp if (!(flags & INTERACTIVE) && isatty(STDIN_FILENO))
340 d947271f 2019-02-08 stsp flags |= INTERACTIVE;
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'))
353 d947271f 2019-02-08 stsp buf_append(bp, buf, len);
355 d947271f 2019-02-08 stsp if (flags & INTERACTIVE)
356 d947271f 2019-02-08 stsp (void)fprintf(stderr, ">> ");
358 d947271f 2019-02-08 stsp buf_putc(bp, '\0');
360 d947271f 2019-02-08 stsp return (buf_release(bp));
364 d947271f 2019-02-08 stsp rcs_rev_select(RCSFILE *file, const char *range)
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;
375 d947271f 2019-02-08 stsp (void)memset(&lnum, 0, sizeof(lnum));
376 d947271f 2019-02-08 stsp (void)memset(&rnum, 0, sizeof(rnum));
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);
384 d947271f 2019-02-08 stsp return (0);
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];
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;
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);
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);
416 d947271f 2019-02-08 stsp rcsnum_cpy(file->rf_head, &rnum, 0);
418 d947271f 2019-02-08 stsp rcs_argv_destroy(revrange);
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;
428 d947271f 2019-02-08 stsp rcs_argv_destroy(revargv);
430 d947271f 2019-02-08 stsp free(lnum.rn_id);
431 d947271f 2019-02-08 stsp free(rnum.rn_id);
433 d947271f 2019-02-08 stsp return (nrev);
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.
444 d947271f 2019-02-08 stsp rcs_set_description(RCSFILE *file, const char *in, int flags)
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";
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. */
464 d947271f 2019-02-08 stsp content = rcs_prompt(prompt, flags);
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);
472 d947271f 2019-02-08 stsp * Split the contents of a file into a list of lines.
474 d947271f 2019-02-08 stsp struct rcs_lines *
475 d947271f 2019-02-08 stsp rcs_splitlines(u_char *data, size_t len)
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;
482 d947271f 2019-02-08 stsp lines = xcalloc(1, sizeof(*lines));
483 d947271f 2019-02-08 stsp TAILQ_INIT(&(lines->l_lines));
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);
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);
503 d947271f 2019-02-08 stsp return (lines);
507 d947271f 2019-02-08 stsp rcs_freelines(struct rcs_lines *lines)
509 d947271f 2019-02-08 stsp struct rcs_line *lp;
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);
516 d947271f 2019-02-08 stsp free(lines);
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 *))
523 d947271f 2019-02-08 stsp struct rcs_lines *dlines, *plines;
524 d947271f 2019-02-08 stsp struct rcs_line *lp;
527 d947271f 2019-02-08 stsp dlines = rcs_splitlines(data, dlen);
528 d947271f 2019-02-08 stsp plines = rcs_splitlines(patch, plen);
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);
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)
540 d947271f 2019-02-08 stsp buf_append(res, lp->l_line, lp->l_len);
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);
549 d947271f 2019-02-08 stsp * rcs_yesno()
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.
556 d947271f 2019-02-08 stsp rcs_yesno(int defc)
558 d947271f 2019-02-08 stsp int c, ret;
560 d947271f 2019-02-08 stsp fflush(stderr);
561 d947271f 2019-02-08 stsp fflush(stdout);
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;
571 d947271f 2019-02-08 stsp while (c != EOF && c != '\n')
572 d947271f 2019-02-08 stsp c = getchar();
574 d947271f 2019-02-08 stsp return (ret);
578 d947271f 2019-02-08 stsp * rcs_strsplit()
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.
583 d947271f 2019-02-08 stsp struct rcs_argvector *
584 d947271f 2019-02-08 stsp rcs_strsplit(const char *str, const char *sep)
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;
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)));
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)));
600 d947271f 2019-02-08 stsp av->argv[i] = NULL;
602 d947271f 2019-02-08 stsp return (av);
606 d947271f 2019-02-08 stsp * rcs_argv_destroy()
608 d947271f 2019-02-08 stsp * Free an argument vector previously allocated by rcs_strsplit().
611 d947271f 2019-02-08 stsp rcs_argv_destroy(struct rcs_argvector *av)
613 d947271f 2019-02-08 stsp free(av->str);
614 d947271f 2019-02-08 stsp free(av->argv);
619 d947271f 2019-02-08 stsp * Strip suffix from filename.
622 d947271f 2019-02-08 stsp rcs_strip_suffix(char *filename)
624 d947271f 2019-02-08 stsp char *p, *suffixes, *next, *ext;
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)) {
634 d947271f 2019-02-08 stsp free(suffixes);