readtextfile.c 3.52 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*  This file is part of "reprepro"
 *  Copyright (C) 2007 Bernhard R. Link
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include "error.h"
#include "names.h"
#include "chunks.h"
#include "readtextfile.h"

/* This file supplies code to read a text file (.changes, .dsc, Release, ...)
 * into a chunk, warning if it is too long or if it contains binary data */

static bool isbinarydata(const char *buffer, size_t len, const char *source) {
	size_t i;
	unsigned char c;

39
	for (i = 0 ; i < len ; i++) {
40
		c = (unsigned char)buffer[i];
41 42 43
		if (c < ' ' && c != '\t' && c != '\n' && c != '\r') {
			fprintf(stderr,
"Unexpected binary character \\%03hho in %s\n",
44 45 46 47 48 49 50 51 52 53 54 55 56
					c, source);
			return true;
		}
	}
	return false;
}

retvalue readtextfilefd(int fd, const char *source, char **data, size_t *len) {
	size_t buffersize = 102400, readdata = 0;
	ssize_t readbytes;
	char *buffer, *h;

	buffer = malloc(buffersize);
57
	if (FAILEDTOALLOC(buffer))
58 59
		return RET_ERROR_OOM;
	errno = 0;
60 61
	while ((readbytes = read(fd, buffer + readdata, buffersize-readdata))
			> 0) {
62

63
		/* text files are normally small, so it does not hurt to check
64
		 * the whole of them always */
65
		if (isbinarydata(buffer + readdata, (size_t)readbytes, source)) {
66 67 68 69
			free(buffer);
			return RET_ERROR;
		}
		readdata += readbytes;
70 71 72
		assert (readdata <= buffersize);
		if (readdata + 1024 >= buffersize) {
			if (buffersize >= 10*1024*1024) {
Bernhard Link's avatar
Bernhard Link committed
73
				fprintf(stderr, "Ridiculously large %s\n", source);
74 75 76 77 78
				free(buffer);
				return RET_ERROR;
			}
			buffersize += 51200;
			h = realloc(buffer, buffersize);
79
			if (FAILEDTOALLOC(h)) {
80 81 82 83 84 85
				free(buffer);
				return RET_ERROR_OOM;
			}
			buffer = h;
		}
	}
86
	if (readbytes < 0) {
87 88 89 90 91 92 93
		int e = errno;
		free(buffer);
		fprintf(stderr, "Error reading %s: %s\n", source,
				strerror(e));
		return RET_ERRNO(e);
	}
	h = realloc(buffer, readdata + 1);
94
	if (h == NULL) {
95 96 97
#ifdef SPLINT
		h = NULL;
#endif
98
		if (readdata >= buffersize) {
99 100 101 102 103 104 105
			free(buffer);
			return RET_ERROR_OOM;
		}
	} else
		buffer = h;
	buffer[readdata] = '\0';
	*data = buffer;
106
	if (len != NULL)
107 108 109 110 111 112 113 114 115 116
		*len = readdata;
	return RET_OK;
}

retvalue readtextfile(const char *source, const char *sourcetoshow, char **data, size_t *len) {
	int fd; char *buffer; size_t bufferlen;
	retvalue r;
	int ret;

	fd = open(source, O_RDONLY|O_NOCTTY);
117
	if (fd < 0) {
118 119 120 121 122 123
		int e = errno;
		fprintf(stderr, "Error opening '%s': %s\n",
				sourcetoshow, strerror(e));
		return RET_ERRNO(e);
	}
	r = readtextfilefd(fd, sourcetoshow, &buffer, &bufferlen);
124
	if (!RET_IS_OK(r)) {
125 126 127 128
		(void)close(fd);
		return r;
	}
	ret = close(fd);
129
	if (ret != 0) {
130 131 132 133 134 135 136
		int e = errno;
		free(buffer);
		fprintf(stderr, "Error reading %s: %s\n", sourcetoshow,
				strerror(e));
		return RET_ERRNO(e);
	}
	*data = buffer;
137
	if (len != NULL)
138 139 140
		*len = bufferlen;
	return RET_OK;
}