1/*
2   Unix SMB/CIFS implementation.
3   stdio replacement
4   Copyright (C) Andrew Tridgell 2001
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21/*
22  stdio is very convenient, but on some systems the file descriptor
23  in FILE* is 8 bits, so it fails when more than 255 files are open.
24
25  XFILE replaces stdio. It is less efficient, but at least it works
26  when you have lots of files open
27
28  The main restriction on XFILE is that it doesn't support seeking,
29  and doesn't support O_RDWR. That keeps the code simple.
30*/
31
32#include "includes.h"
33
34#define XBUFSIZE BUFSIZ
35
36static XFILE _x_stdin =  { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
37static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
38static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
39
40XFILE *x_stdin = &_x_stdin;
41XFILE *x_stdout = &_x_stdout;
42XFILE *x_stderr = &_x_stderr;
43
44#define X_FLAG_EOF 1
45#define X_FLAG_ERROR 2
46#define X_FLAG_EINVAL 3
47
48/* simulate setvbuf() */
49int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
50{
51	x_fflush(f);
52	if (f->bufused) return -1;
53
54	/* on files being read full buffering is the only option */
55	if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
56		mode = X_IOFBF;
57	}
58
59	/* destroy any earlier buffer */
60	SAFE_FREE(f->buf);
61	f->buf = 0;
62	f->bufsize = 0;
63	f->next = NULL;
64	f->bufused = 0;
65	f->buftype = mode;
66
67	if (f->buftype == X_IONBF) return 0;
68
69	/* if buffering then we need some size */
70	if (size == 0) size = XBUFSIZE;
71
72	f->bufsize = size;
73	f->bufused = 0;
74
75	return 0;
76}
77
78/* allocate the buffer */
79static int x_allocate_buffer(XFILE *f)
80{
81	if (f->buf) return 1;
82	if (f->bufsize == 0) return 0;
83	f->buf = malloc(f->bufsize);
84	if (!f->buf) return 0;
85	f->next = f->buf;
86	return 1;
87}
88
89
90/* this looks more like open() than fopen(), but that is quite deliberate.
91   I want programmers to *think* about O_EXCL, O_CREAT etc not just
92   get them magically added
93*/
94XFILE *x_fopen(const char *fname, int flags, mode_t mode)
95{
96	XFILE *ret;
97
98	ret = (XFILE *)malloc(sizeof(XFILE));
99	if (!ret) return NULL;
100
101	memset(ret, 0, sizeof(XFILE));
102
103	if ((flags & O_ACCMODE) == O_RDWR) {
104		/* we don't support RDWR in XFILE - use file
105		   descriptors instead */
106		errno = EINVAL;
107		return NULL;
108	}
109
110	ret->open_flags = flags;
111
112	ret->fd = sys_open(fname, flags, mode);
113	if (ret->fd == -1) {
114		SAFE_FREE(ret);
115		return NULL;
116	}
117
118	x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
119
120	return ret;
121}
122
123/* simulate fclose() */
124int x_fclose(XFILE *f)
125{
126	int ret;
127
128	/* make sure we flush any buffered data */
129	x_fflush(f);
130
131	ret = close(f->fd);
132	f->fd = -1;
133	if (f->buf) {
134		/* make sure data can't leak into a later malloc */
135		memset(f->buf, 0, f->bufsize);
136		SAFE_FREE(f->buf);
137	}
138	SAFE_FREE(f);
139	return ret;
140}
141
142/* simulate fwrite() */
143size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
144{
145	ssize_t ret;
146	size_t total=0;
147
148	/* we might be writing unbuffered */
149	if (f->buftype == X_IONBF ||
150	    (!f->buf && !x_allocate_buffer(f))) {
151		ret = write(f->fd, p, size*nmemb);
152		if (ret == -1) return -1;
153		return ret/size;
154	}
155
156
157	while (total < size*nmemb) {
158		size_t n = f->bufsize - f->bufused;
159		n = MIN(n, (size*nmemb)-total);
160
161		if (n == 0) {
162			/* it's full, flush it */
163			x_fflush(f);
164			continue;
165		}
166
167		memcpy(f->buf + f->bufused, total+(const char *)p, n);
168		f->bufused += n;
169		total += n;
170	}
171
172	/* when line buffered we need to flush at the last linefeed. This can
173	   flush a bit more than necessary, but that is harmless */
174	if (f->buftype == X_IOLBF && f->bufused) {
175		int i;
176		for (i=(size*nmemb)-1; i>=0; i--) {
177			if (*(i+(const char *)p) == '\n') {
178				x_fflush(f);
179				break;
180			}
181		}
182	}
183
184	return total/size;
185}
186
187/* thank goodness for asprintf() */
188 int x_vfprintf(XFILE *f, const char *format, va_list ap)
189{
190	char *p;
191	int len, ret;
192	va_list ap2;
193
194	VA_COPY(ap2, ap);
195
196	len = vasprintf(&p, format, ap2);
197	if (len <= 0) return len;
198	ret = x_fwrite(p, 1, len, f);
199	SAFE_FREE(p);
200	return ret;
201}
202
203 int x_fprintf(XFILE *f, const char *format, ...)
204{
205	va_list ap;
206	int ret;
207
208	va_start(ap, format);
209	ret = x_vfprintf(f, format, ap);
210	va_end(ap);
211	return ret;
212}
213
214/* at least fileno() is simple! */
215int x_fileno(XFILE *f)
216{
217	return f->fd;
218}
219
220/* simulate fflush() */
221int x_fflush(XFILE *f)
222{
223	int ret;
224
225	if (f->flags & X_FLAG_ERROR) return -1;
226
227	if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
228		errno = EINVAL;
229		return -1;
230	}
231
232	if (f->bufused == 0) return 0;
233
234	ret = write(f->fd, f->buf, f->bufused);
235	if (ret == -1) return -1;
236
237	f->bufused -= ret;
238	if (f->bufused > 0) {
239		f->flags |= X_FLAG_ERROR;
240		memmove(f->buf, ret + (char *)f->buf, f->bufused);
241		return -1;
242	}
243
244	return 0;
245}
246
247/* simulate setbuffer() */
248void x_setbuffer(XFILE *f, char *buf, size_t size)
249{
250	x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
251}
252
253/* simulate setbuf() */
254void x_setbuf(XFILE *f, char *buf)
255{
256	x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
257}
258
259/* simulate setlinebuf() */
260void x_setlinebuf(XFILE *f)
261{
262	x_setvbuf(f, NULL, X_IOLBF, 0);
263}
264
265
266/* simulate feof() */
267int x_feof(XFILE *f)
268{
269	if (f->flags & X_FLAG_EOF) return 1;
270	return 0;
271}
272
273/* simulate ferror() */
274int x_ferror(XFILE *f)
275{
276	if (f->flags & X_FLAG_ERROR) return 1;
277	return 0;
278}
279
280/* fill the read buffer */
281static void x_fillbuf(XFILE *f)
282{
283	int n;
284
285	if (f->bufused) return;
286
287	if (!f->buf && !x_allocate_buffer(f)) return;
288
289	n = read(f->fd, f->buf, f->bufsize);
290	if (n <= 0) return;
291	f->bufused = n;
292	f->next = f->buf;
293}
294
295/* simulate fgetc() */
296int x_fgetc(XFILE *f)
297{
298	int ret;
299
300	if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
301
302	if (f->bufused == 0) x_fillbuf(f);
303
304	if (f->bufused == 0) {
305		f->flags |= X_FLAG_EOF;
306		return EOF;
307	}
308
309	ret = *(unsigned char *)(f->next);
310	f->next++;
311	f->bufused--;
312	return ret;
313}
314
315/* simulate fread */
316size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
317{
318	size_t total = 0;
319	while (total < size*nmemb) {
320		int c = x_fgetc(f);
321		if (c == EOF) break;
322		(total+(char *)p)[0] = (char)c;
323		total++;
324	}
325	return total/size;
326}
327
328/* simulate fgets() */
329char *x_fgets(char *s, int size, XFILE *stream)
330{
331	char *s0 = s;
332	int l = size;
333	while (l>1) {
334		int c = x_fgetc(stream);
335		if (c == EOF) break;
336		*s++ = (char)c;
337		l--;
338		if (c == '\n') break;
339	}
340	if (l==size || x_ferror(stream)) {
341		return 0;
342	}
343	*s = 0;
344	return s0;
345}
346
347/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
348 * set then an error is returned */
349off_t x_tseek(XFILE *f, off_t offset, int whence)
350{
351	if (f->flags & X_FLAG_ERROR)
352		return -1;
353
354	/* only SEEK_SET and SEEK_END are supported */
355	/* SEEK_CUR needs internal offset counter */
356	if (whence != SEEK_SET && whence != SEEK_END) {
357		f->flags |= X_FLAG_EINVAL;
358		errno = EINVAL;
359		return -1;
360	}
361
362	/* empty the buffer */
363	switch (f->open_flags & O_ACCMODE) {
364	case O_RDONLY:
365		f->bufused = 0;
366		break;
367	case O_WRONLY:
368		if (x_fflush(f) != 0)
369			return -1;
370		break;
371	default:
372		errno = EINVAL;
373		return -1;
374	}
375
376	f->flags &= ~X_FLAG_EOF;
377	return (off_t)sys_lseek(f->fd, offset, whence);
378}
379