1 /* $OpenBSD: util.c,v 1.24 2003/07/31 20:51:43 otto Exp $ */ 2 3 #ifndef lint 4 static const char rcsid[] = "$OpenBSD: util.c,v 1.24 2003/07/31 20:51:43 otto Exp $"; 5 #endif /* not lint */ 6 7 #include <sys/param.h> 8 #include <sys/stat.h> 9 10 #include <ctype.h> 11 #include <errno.h> 12 #include <fcntl.h> 13 #include <libgen.h> 14 #include <paths.h> 15 #include <stdarg.h> 16 #include <stdlib.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 #include "common.h" 22 #include "util.h" 23 #include "backupfile.h" 24 #include "pathnames.h" 25 26 27 /* Rename a file, copying it if necessary. */ 28 29 int 30 move_file(const char *from, const char *to) 31 { 32 int fromfd; 33 ssize_t i; 34 35 /* to stdout? */ 36 37 if (strEQ(to, "-")) { 38 #ifdef DEBUGGING 39 if (debug & 4) 40 say("Moving %s to stdout.\n", from); 41 #endif 42 fromfd = open(from, O_RDONLY); 43 if (fromfd < 0) 44 pfatal("internal error, can't reopen %s", from); 45 while ((i = read(fromfd, buf, sizeof buf)) > 0) 46 if (write(STDOUT_FILENO, buf, i) != i) 47 pfatal("write failed"); 48 close(fromfd); 49 return 0; 50 } 51 if (backup_file(to) < 0) { 52 say("Can't backup %s, output is in %s: %s\n", to, from, 53 strerror(errno)); 54 return -1; 55 } 56 #ifdef DEBUGGING 57 if (debug & 4) 58 say("Moving %s to %s.\n", from, to); 59 #endif 60 if (rename(from, to) < 0) { 61 if (errno != EXDEV || copy_file(from, to) < 0) { 62 say("Can't create %s, output is in %s: %s\n", 63 to, from, strerror(errno)); 64 return -1; 65 } 66 } 67 return 0; 68 } 69 70 /* Backup the original file. */ 71 72 int 73 backup_file(const char *orig) 74 { 75 struct stat filestat; 76 char bakname[MAXPATHLEN], *s, *simplename; 77 dev_t orig_device; 78 ino_t orig_inode; 79 80 if (backup_type == none || stat(orig, &filestat) != 0) 81 return 0; /* nothing to do */ 82 orig_device = filestat.st_dev; 83 orig_inode = filestat.st_ino; 84 85 if (origprae) { 86 if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) || 87 strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname)) 88 fatal("filename %s too long for buffer\n", origprae); 89 } else { 90 if ((s = find_backup_file_name(orig)) == NULL) 91 fatal("out of memory\n"); 92 if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname)) 93 fatal("filename %s too long for buffer\n", s); 94 free(s); 95 } 96 97 if ((simplename = strrchr(bakname, '/')) != NULL) 98 simplename = simplename + 1; 99 else 100 simplename = bakname; 101 102 /* 103 * Find a backup name that is not the same file. Change the 104 * first lowercase char into uppercase; if that isn't 105 * sufficient, chop off the first char and try again. 106 */ 107 while (stat(bakname, &filestat) == 0 && 108 orig_device == filestat.st_dev && orig_inode == filestat.st_ino) { 109 /* Skip initial non-lowercase chars. */ 110 for (s = simplename; *s && !islower(*s); s++) 111 ; 112 if (*s) 113 *s = toupper(*s); 114 else 115 memmove(simplename, simplename + 1, 116 strlen(simplename + 1) + 1); 117 } 118 #ifdef DEBUGGING 119 if (debug & 4) 120 say("Moving %s to %s.\n", orig, bakname); 121 #endif 122 if (rename(orig, bakname) < 0) { 123 if (errno != EXDEV || copy_file(orig, bakname) < 0) 124 return -1; 125 } 126 return 0; 127 } 128 129 /* 130 * Copy a file. 131 */ 132 int 133 copy_file(const char *from, const char *to) 134 { 135 int tofd, fromfd; 136 ssize_t i; 137 138 tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666); 139 if (tofd < 0) 140 return -1; 141 fromfd = open(from, O_RDONLY, 0); 142 if (fromfd < 0) 143 pfatal("internal error, can't reopen %s", from); 144 while ((i = read(fromfd, buf, sizeof buf)) > 0) 145 if (write(tofd, buf, i) != i) 146 pfatal("write to %s failed", to); 147 close(fromfd); 148 close(tofd); 149 return 0; 150 } 151 152 /* 153 * Allocate a unique area for a string. 154 */ 155 char * 156 savestr(const char *s) 157 { 158 char *rv; 159 160 if (!s) 161 s = "Oops"; 162 rv = strdup(s); 163 if (rv == NULL) { 164 if (using_plan_a) 165 out_of_mem = TRUE; 166 else 167 fatal("out of memory\n"); 168 } 169 return rv; 170 } 171 172 /* 173 * Vanilla terminal output (buffered). 174 */ 175 void 176 say(const char *fmt, ...) 177 { 178 va_list ap; 179 180 va_start(ap, fmt); 181 vfprintf(stderr, fmt, ap); 182 va_end(ap); 183 fflush(stderr); 184 } 185 186 /* 187 * Terminal output, pun intended. 188 */ 189 void 190 fatal(const char *fmt, ...) 191 { 192 va_list ap; 193 194 va_start(ap, fmt); 195 fprintf(stderr, "patch: **** "); 196 vfprintf(stderr, fmt, ap); 197 va_end(ap); 198 my_exit(2); 199 } 200 201 /* 202 * Say something from patch, something from the system, then silence . . . 203 */ 204 void 205 pfatal(const char *fmt, ...) 206 { 207 va_list ap; 208 int errnum = errno; 209 210 fprintf(stderr, "patch: **** "); 211 va_start(ap, fmt); 212 vfprintf(stderr, fmt, ap); 213 va_end(ap); 214 fprintf(stderr, ": %s\n", strerror(errnum)); 215 my_exit(2); 216 } 217 218 /* 219 * Get a response from the user via /dev/tty 220 */ 221 void 222 ask(const char *fmt, ...) 223 { 224 va_list ap; 225 ssize_t nr; 226 static int ttyfd = -1; 227 228 va_start(ap, fmt); 229 vfprintf(stdout, fmt, ap); 230 va_end(ap); 231 fflush(stdout); 232 if (ttyfd < 0) 233 ttyfd = open(_PATH_TTY, O_RDONLY); 234 if (ttyfd >= 0) { 235 if ((nr = read(ttyfd, buf, sizeof(buf))) > 0 && 236 buf[nr - 1] == '\n') 237 buf[nr - 1] = '\0'; 238 } 239 if (ttyfd < 0 || nr <= 0) { 240 /* no tty or error reading, pretend user entered 'return' */ 241 putchar('\n'); 242 buf[0] = '\0'; 243 } 244 } 245 246 /* 247 * How to handle certain events when not in a critical region. 248 */ 249 void 250 set_signals(int reset) 251 { 252 static sig_t hupval, intval; 253 254 if (!reset) { 255 hupval = signal(SIGHUP, SIG_IGN); 256 if (hupval != SIG_IGN) 257 hupval = (sig_t) my_exit; 258 intval = signal(SIGINT, SIG_IGN); 259 if (intval != SIG_IGN) 260 intval = (sig_t) my_exit; 261 } 262 signal(SIGHUP, hupval); 263 signal(SIGINT, intval); 264 } 265 266 /* 267 * How to handle certain events when in a critical region. 268 */ 269 void 270 ignore_signals(void) 271 { 272 signal(SIGHUP, SIG_IGN); 273 signal(SIGINT, SIG_IGN); 274 } 275 276 /* 277 * Make sure we'll have the directories to create a file. If `striplast' is 278 * TRUE, ignore the last element of `filename'. 279 */ 280 281 void 282 makedirs(const char *filename, bool striplast) 283 { 284 char *tmpbuf; 285 286 if ((tmpbuf = strdup(filename)) == NULL) 287 fatal("out of memory\n"); 288 289 if (striplast) { 290 char *s = strrchr(tmpbuf, '/'); 291 if (s == NULL) 292 return; /* nothing to be done */ 293 *s = '\0'; 294 } 295 if (snprintf(buf, sizeof(buf), "%s -p %s", _PATH_MKDIR, tmpbuf) 296 >= sizeof(buf)) 297 fatal("buffer too small to hold %.20s...\n", tmpbuf); 298 299 if (system(buf)) 300 pfatal("%.40s failed", buf); 301 } 302 303 /* 304 * Make filenames more reasonable. 305 */ 306 char * 307 fetchname(const char *at, int strip_leading, int assume_exists) 308 { 309 char *fullname, *name, *t, tmpbuf[200]; 310 int sleading; 311 struct stat filestat; 312 313 if (at == NULL || *at == '\0') 314 return NULL; 315 while (isspace(*at)) 316 at++; 317 #ifdef DEBUGGING 318 if (debug & 128) 319 say("fetchname %s %d %d\n", at, strip_leading, assume_exists); 320 #endif 321 /* So files can be created by diffing against /dev/null. */ 322 if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1)) 323 return NULL; 324 name = fullname = t = savestr(at); 325 326 /* Strip off up to `strip_leading' path components and NUL terminate. */ 327 for (sleading = strip_leading; *t != '\0' && !isspace(*t); t++) { 328 if (t[0] == '/' && t[1] != '/' && t[1] != '\0') 329 if (--sleading >= 0) 330 name = t + 1; 331 } 332 *t = '\0'; 333 334 /* 335 * If no -p option was given (957 is the default value!), we were 336 * given a relative pathname, and the leading directories that we 337 * just stripped off all exist, put them back on. 338 */ 339 if (strip_leading == 957 && name != fullname && *fullname != '/') { 340 name[-1] = '\0'; 341 if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { 342 name[-1] = '/'; 343 name = fullname; 344 } 345 } 346 name = savestr(name); 347 free(fullname); 348 349 if (stat(name, &filestat) && !assume_exists) { 350 char *filebase = basename(name); 351 char *filedir = dirname(name); 352 353 #define try(f, a1, a2, a3) \ 354 (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) 355 356 if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || 357 try("%s/RCS/%s%s", filedir, filebase, "") || 358 try("%s/%s%s", filedir, filebase, RCSSUFFIX) || 359 try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || 360 try("%s/%s%s", filedir, SCCSPREFIX, filebase)) 361 return name; 362 free(name); 363 name = NULL; 364 } 365 return name; 366 } 367 368 void 369 version(void) 370 { 371 fprintf(stderr, "Patch version 2.0-12u8-OpenBSD\n"); 372 my_exit(EXIT_SUCCESS); 373 } 374 375 /* 376 * Exit with cleanup. 377 */ 378 void 379 my_exit(int status) 380 { 381 unlink(TMPINNAME); 382 if (!toutkeep) 383 unlink(TMPOUTNAME); 384 if (!trejkeep) 385 unlink(TMPREJNAME); 386 unlink(TMPPATNAME); 387 exit(status); 388 } 389