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 = (char *)SMB_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 = SMB_MALLOC_P(XFILE);
99	if (!ret) {
100		return NULL;
101	}
102
103	memset(ret, 0, sizeof(XFILE));
104
105	if ((flags & O_ACCMODE) == O_RDWR) {
106		/* we don't support RDWR in XFILE - use file
107		   descriptors instead */
108		SAFE_FREE(ret);
109		errno = EINVAL;
110		return NULL;
111	}
112
113	ret->open_flags = flags;
114
115	ret->fd = sys_open(fname, flags, mode);
116	if (ret->fd == -1) {
117		SAFE_FREE(ret);
118		return NULL;
119	}
120
121	x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
122
123	return ret;
124}
125
126XFILE *x_fdup(const XFILE *f)
127{
128	XFILE *ret;
129	int fd;
130
131	fd = dup(x_fileno(f));
132	if (fd < 0) {
133		return NULL;
134	}
135
136	ret = SMB_CALLOC_ARRAY(XFILE, 1);
137	if (!ret) {
138		close(fd);
139		return NULL;
140	}
141
142	ret->fd = fd;
143	ret->open_flags = f->open_flags;
144	x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
145	return ret;
146}
147
148/* simulate fclose() */
149int x_fclose(XFILE *f)
150{
151	int ret;
152
153	/* make sure we flush any buffered data */
154	x_fflush(f);
155
156	ret = close(f->fd);
157	f->fd = -1;
158	if (f->buf) {
159		/* make sure data can't leak into a later malloc */
160		memset(f->buf, 0, f->bufsize);
161		SAFE_FREE(f->buf);
162	}
163	/* check the file descriptor given to the function is NOT one of the static
164	 * descriptor of this libreary or we will free unallocated memory
165	 * --sss */
166	if (f != x_stdin && f != x_stdout && f != x_stderr) {
167		SAFE_FREE(f);
168	}
169	return ret;
170}
171
172/* simulate fwrite() */
173size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
174{
175	ssize_t ret;
176	size_t total=0;
177
178	/* we might be writing unbuffered */
179	if (f->buftype == X_IONBF ||
180	    (!f->buf && !x_allocate_buffer(f))) {
181		ret = write(f->fd, p, size*nmemb);
182		if (ret == -1) return -1;
183		return ret/size;
184	}
185
186
187	while (total < size*nmemb) {
188		size_t n = f->bufsize - f->bufused;
189		n = MIN(n, (size*nmemb)-total);
190
191		if (n == 0) {
192			/* it's full, flush it */
193			x_fflush(f);
194			continue;
195		}
196
197		memcpy(f->buf + f->bufused, total+(const char *)p, n);
198		f->bufused += n;
199		total += n;
200	}
201
202	/* when line buffered we need to flush at the last linefeed. This can
203	   flush a bit more than necessary, but that is harmless */
204	if (f->buftype == X_IOLBF && f->bufused) {
205		int i;
206		for (i=(size*nmemb)-1; i>=0; i--) {
207			if (*(i+(const char *)p) == '\n') {
208				x_fflush(f);
209				break;
210			}
211		}
212	}
213
214	return total/size;
215}
216
217/* thank goodness for asprintf() */
218 int x_vfprintf(XFILE *f, const char *format, va_list ap)
219{
220	char *p;
221	int len, ret;
222	va_list ap2;
223
224	VA_COPY(ap2, ap);
225
226	len = vasprintf(&p, format, ap2);
227	if (len <= 0) return len;
228	ret = x_fwrite(p, 1, len, f);
229	SAFE_FREE(p);
230	return ret;
231}
232
233 int x_fprintf(XFILE *f, const char *format, ...)
234{
235	va_list ap;
236	int ret;
237
238	va_start(ap, format);
239	ret = x_vfprintf(f, format, ap);
240	va_end(ap);
241	return ret;
242}
243
244/* at least fileno() is simple! */
245int x_fileno(const XFILE *f)
246{
247	return f->fd;
248}
249
250/* simulate fflush() */
251int x_fflush(XFILE *f)
252{
253	int ret;
254
255	if (f->flags & X_FLAG_ERROR) return -1;
256
257	if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
258		errno = EINVAL;
259		return -1;
260	}
261
262	if (f->bufused == 0 || !f->buf) return 0;
263
264	ret = write(f->fd, f->buf, f->bufused);
265	if (ret == -1) return -1;
266
267	f->bufused -= ret;
268	if (f->bufused > 0) {
269		f->flags |= X_FLAG_ERROR;
270		memmove(f->buf, ret + (char *)f->buf, f->bufused);
271		return -1;
272	}
273
274	return 0;
275}
276
277/* simulate setbuffer() */
278void x_setbuffer(XFILE *f, char *buf, size_t size)
279{
280	x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
281}
282
283/* simulate setbuf() */
284void x_setbuf(XFILE *f, char *buf)
285{
286	x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
287}
288
289/* simulate setlinebuf() */
290void x_setlinebuf(XFILE *f)
291{
292	x_setvbuf(f, NULL, X_IOLBF, 0);
293}
294
295
296/* simulate feof() */
297int x_feof(XFILE *f)
298{
299	if (f->flags & X_FLAG_EOF) return 1;
300	return 0;
301}
302
303/* simulate ferror() */
304int x_ferror(XFILE *f)
305{
306	if (f->flags & X_FLAG_ERROR) return 1;
307	return 0;
308}
309
310/* fill the read buffer */
311static void x_fillbuf(XFILE *f)
312{
313	int n;
314
315	if (f->bufused) return;
316
317	if (!f->buf && !x_allocate_buffer(f)) return;
318
319	n = read(f->fd, f->buf, f->bufsize);
320	if (n <= 0) return;
321	f->bufused = n;
322	f->next = f->buf;
323}
324
325/* simulate fgetc() */
326int x_fgetc(XFILE *f)
327{
328	int ret;
329
330	if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
331
332	if (f->bufused == 0) x_fillbuf(f);
333
334	if (f->bufused == 0) {
335		f->flags |= X_FLAG_EOF;
336		return EOF;
337	}
338
339	ret = *(unsigned char *)(f->next);
340	f->next++;
341	f->bufused--;
342	return ret;
343}
344
345/* simulate fread */
346size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
347{
348	size_t total = 0;
349	while (total < size*nmemb) {
350		int c = x_fgetc(f);
351		if (c == EOF) break;
352		(total+(char *)p)[0] = (char)c;
353		total++;
354	}
355	return total/size;
356}
357
358/* simulate fgets() */
359char *x_fgets(char *s, int size, XFILE *stream)
360{
361	char *s0 = s;
362	int l = size;
363	while (l>1) {
364		int c = x_fgetc(stream);
365		if (c == EOF) break;
366		*s++ = (char)c;
367		l--;
368		if (c == '\n') break;
369	}
370	if (l==size || x_ferror(stream)) {
371		return 0;
372	}
373	*s = 0;
374	return s0;
375}
376
377/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
378 * set then an error is returned */
379off_t x_tseek(XFILE *f, off_t offset, int whence)
380{
381	if (f->flags & X_FLAG_ERROR)
382		return -1;
383
384	/* only SEEK_SET and SEEK_END are supported */
385	/* SEEK_CUR needs internal offset counter */
386	if (whence != SEEK_SET && whence != SEEK_END) {
387		f->flags |= X_FLAG_EINVAL;
388		errno = EINVAL;
389		return -1;
390	}
391
392	/* empty the buffer */
393	switch (f->open_flags & O_ACCMODE) {
394	case O_RDONLY:
395		f->bufused = 0;
396		break;
397	case O_WRONLY:
398		if (x_fflush(f) != 0)
399			return -1;
400		break;
401	default:
402		errno = EINVAL;
403		return -1;
404	}
405
406	f->flags &= ~X_FLAG_EOF;
407	return (off_t)sys_lseek(f->fd, offset, whence);
408}
409