1/* 2 * Copyright (c) 2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <stdio.h> 25#include <stdlib.h> 26#include <unistd.h> 27#include <string.h> 28#include <stdbool.h> 29#include <errno.h> 30#include <err.h> 31#include <sysexits.h> 32 33#include <sys/stat.h> 34#include <sys/fcntl.h> 35#include <sys/param.h> 36#include <sys/time.h> 37 38void usage(void); 39 40int main(int argc, char * argv[]) 41{ 42 struct stat sb; 43 char *newcontent = NULL; 44 size_t newcontentlength = 0; 45 char *oldcontent = NULL; 46 int ret; 47 int dstfd; 48 const char *dst = NULL; 49 ssize_t readsize, writesize; 50 int i; 51 52 if (argc < 2) { 53 usage(); 54 } 55 56 dst = argv[1]; 57 58 for (i=2; i < argc; i++) { 59 newcontentlength += strlen(argv[i]) + 1 /* space or newline */; 60 } 61 newcontentlength += 1; /* NUL */ 62 63 newcontent = malloc(newcontentlength); 64 if (newcontent == NULL) 65 err(EX_UNAVAILABLE, "malloc() failed"); 66 67 newcontent[0] = '\0'; 68 69 for (i=2; i < argc; i++) { 70 strlcat(newcontent, argv[i], newcontentlength); 71 if (i < argc - 1) { 72 strlcat(newcontent, " ", newcontentlength); 73 } else { 74 strlcat(newcontent, "\n", newcontentlength); 75 } 76 } 77 78 dstfd = open(dst, O_RDWR | O_CREAT | O_APPEND, DEFFILEMODE); 79 if (dstfd < 0) 80 err(EX_NOINPUT, "open(%s)", dst); 81 82 ret = fstat(dstfd, &sb); 83 if (ret < 0) 84 err(EX_NOINPUT, "fstat(%s)", dst); 85 86 if (!S_ISREG(sb.st_mode)) 87 err(EX_USAGE, "%s is not a regular file", dst); 88 89 if (sb.st_size != newcontentlength) { 90 /* obvious new content must be different than old */ 91 goto replace; 92 } 93 94 oldcontent = malloc(newcontentlength); 95 if (oldcontent == NULL) 96 err(EX_UNAVAILABLE, "malloc(%lu) failed", newcontentlength); 97 98 readsize = read(dstfd, oldcontent, newcontentlength); 99 if (readsize == -1) 100 err(EX_UNAVAILABLE, "read() failed"); 101 else if (readsize != newcontentlength) 102 errx(EX_UNAVAILABLE, "short read of file"); 103 104 if (0 == memcmp(oldcontent, newcontent, newcontentlength)) { 105 /* binary comparison succeeded, just exit */ 106 free(oldcontent); 107 ret = close(dstfd); 108 if (ret < 0) 109 err(EX_UNAVAILABLE, "close() failed"); 110 111 exit(0); 112 } 113 114replace: 115 ret = ftruncate(dstfd, 0); 116 if (ret < 0) 117 err(EX_UNAVAILABLE, "ftruncate() failed"); 118 119 writesize = write(dstfd, newcontent, newcontentlength); 120 if (writesize == -1) 121 err(EX_UNAVAILABLE, "write() failed"); 122 else if (writesize != newcontentlength) 123 errx(EX_UNAVAILABLE, "short write of file"); 124 125 ret = close(dstfd); 126 if (ret < 0) 127 err(EX_NOINPUT, "close(dst)"); 128 129 return 0; 130} 131 132void usage(void) 133{ 134 fprintf(stderr, "Usage: %s <dst> <new> <contents> <...>\n", 135 getprogname()); 136 exit(EX_USAGE); 137} 138