1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * "buffered" i/o functions for the standalone environment. (ugh).
27 */
28
29#pragma ident	"%Z%%M%	%I%	%E% SMI"
30
31#include <sys/types.h>
32#include <sys/promif.h>
33#include <sys/varargs.h>
34#include <sys/bootvfs.h>
35#include <sys/salib.h>
36
37enum {
38	F_OPEN		= 0x01,
39	F_ERROR		= 0x02,
40	F_SEEKABLE	= 0x04
41};
42
43FILE	__iob[_NFILE] = {
44	{ F_OPEN, 0, 0, 0, "stdin"	},
45	{ F_OPEN, 1, 0, 0, "stdout"	},
46	{ F_OPEN, 2, 0, 0, "stderr"	}
47};
48
49static boolean_t
50fcheck(FILE *stream, int flags)
51{
52	errno = 0;
53	if ((stream->_flag & flags) != flags) {
54		errno = EBADF;
55		return (B_FALSE);
56	}
57	return (B_TRUE);
58}
59
60int
61fclose(FILE *stream)
62{
63	if (!fcheck(stream, F_OPEN))
64		return (EOF);
65
66	(void) close(stream->_file);
67	stream->_flag = 0;
68	stream->_file = -1;
69	stream->_name[0] = '\0';
70	return (0);
71}
72
73int
74feof(FILE *stream)
75{
76	if (!fcheck(stream, F_OPEN))
77		return (0);
78
79	return (stream->_len == stream->_offset);
80}
81
82int
83ferror(FILE *stream)
84{
85	if (!fcheck(stream, F_OPEN))
86		return (0);
87
88	return ((stream->_flag & F_ERROR) != 0);
89}
90
91void
92clearerr(FILE *stream)
93{
94	stream->_flag &= ~F_ERROR;
95}
96
97int
98fflush(FILE *stream)
99{
100	if (!fcheck(stream, F_OPEN))
101		return (EOF);
102
103	/* Currently, a nop */
104	return (0);
105}
106
107char *
108fgets(char *s, int n, FILE *stream)
109{
110	int	bytes;
111	ssize_t	cnt;
112
113	if (!fcheck(stream, F_OPEN))
114		return (NULL);
115
116	for (bytes = 0; bytes < (n - 1); ++bytes) {
117		cnt = read(stream->_file, &s[bytes], 1);
118		if (cnt < 0) {
119			if (bytes != 0) {
120				s[bytes] = '\0';
121				return (s);
122			} else {
123				stream->_flag |= F_ERROR;
124				return (NULL);
125			}
126		} else if (cnt == 0) {
127			/* EOF */
128			if (bytes != 0) {
129				s[bytes] = '\0';
130				return (s);
131			} else
132				return (NULL);
133		} else {
134			stream->_offset++;
135			if (s[bytes] == '\n') {
136				s[bytes + 1] = '\0';
137				return (s);
138			}
139		}
140	}
141	s[bytes] = '\0';
142	return (s);
143}
144
145/*
146 * We currently only support read-only ("r" mode) opens and unbuffered I/O.
147 */
148FILE *
149fopen(const char *filename, const char *mode)
150{
151	FILE		*stream;
152	const char	*t;
153	int		fd, i;
154
155	errno = 0;
156
157	/*
158	 * Make sure we have a filesystem underneath us before even trying.
159	 */
160	if (get_default_fs() == NULL)
161		return (NULL);
162
163	for (t = mode; t != NULL && *t != '\0'; t++) {
164		switch (*t) {
165		case 'b':
166			/* We ignore this a'la ISO C standard conformance */
167			break;
168		case 'r':
169			/* We ignore this because we always open for reading */
170			break;
171
172		case 'a':
173		case 'w':
174		case '+':
175			errno = EROFS;
176			return (NULL);
177
178		default:
179			errno = EINVAL;
180			return (NULL);
181		}
182	}
183
184	for (i = 0; i < _NFILE; i++) {
185		stream = &__iob[i];
186		if ((stream->_flag & F_OPEN) == 0) {
187			fd = open(filename, O_RDONLY);
188			if (fd < 0)
189				return (NULL);
190
191			stream->_file = fd;
192			stream->_flag |= F_OPEN;
193			(void) strlcpy(stream->_name, filename,
194			    sizeof (stream->_name));
195			return (stream);
196		}
197	}
198
199	errno = EMFILE;
200	return (NULL);
201}
202
203/* PRINTFLIKE1 */
204void
205printf(const char *fmt, ...)
206{
207	va_list adx;
208
209	va_start(adx, fmt);
210	prom_vprintf(fmt, adx);
211	va_end(adx);
212}
213
214/*
215 * Only writing to stderr or stdout is permitted.
216 */
217/* PRINTFLIKE2 */
218int
219fprintf(FILE *stream, const char *format, ...)
220{
221	int	nwritten;
222	va_list	va;
223
224	if (!fcheck(stream, F_OPEN))
225		return (-1);
226
227	/*
228	 * Since fopen() doesn't return writable streams, the only valid
229	 * writable streams are stdout and stderr.
230	 */
231	if (stream != stdout && stream != stderr) {
232		errno = EBADF;
233		return (-1);
234	}
235
236	va_start(va, format);
237	printf(format, va);
238	va_end(va);
239
240	va_start(va, format);
241	nwritten = vsnprintf(NULL, 0, format, va);
242	va_end(va);
243
244	return (nwritten);
245}
246
247size_t
248fread(void *ptr, size_t size, size_t nitems, FILE *stream)
249{
250	size_t	items;
251	ssize_t	bytes, totbytes = 0;
252	char	*strp = ptr;
253
254	if (!fcheck(stream, F_OPEN))
255		return (0);
256
257	for (items = 0, bytes = 0; items < nitems; items++) {
258		bytes = read(stream->_file, &strp[totbytes], size);
259		if (bytes < 0) {
260			stream->_flag |= F_ERROR;
261			return (0);
262		} else if (bytes == 0) {
263			/* EOF */
264			return ((totbytes == 0) ? 0 : totbytes / size);
265		} else if (bytes == size) {
266			stream->_offset += bytes;
267			totbytes += bytes;
268		} else {
269			(void) lseek(stream->_file, stream->_offset, SEEK_SET);
270			return (totbytes / size);
271		}
272	}
273
274	return (totbytes / size);
275}
276
277/*
278 * We don't grow files.
279 */
280int
281fseek(FILE *stream, long offset, int whence)
282{
283	off_t	new_offset, result;
284
285	if (!fcheck(stream, F_OPEN | F_SEEKABLE))
286		return (-1);
287
288	switch (whence) {
289	case SEEK_SET:
290		new_offset = (off_t)offset;
291		break;
292	case SEEK_CUR:
293		new_offset = stream->_offset + (off_t)offset;
294		break;
295	case SEEK_END:
296		new_offset = (off_t)stream->_len + (off_t)offset;
297		break;
298	default:
299		errno = EINVAL;
300		return (-1);
301	}
302
303	if (new_offset > (off_t)stream->_len) {
304		errno = EFBIG;
305	} else if (new_offset < 0L) {
306		errno = EOVERFLOW;
307	} else {
308		errno = 0;
309	}
310
311	result = lseek(stream->_file, new_offset, SEEK_SET);
312	if (result >= 0)
313		stream->_offset = result;
314	else
315		stream->_flag |= F_ERROR;
316
317	return (result);
318}
319
320long
321ftell(FILE *stream)
322{
323	if (!fcheck(stream, F_OPEN | F_SEEKABLE))
324		return (0);
325
326	return ((long)stream->_offset);
327}
328
329size_t
330fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream)
331{
332	if (!fcheck(stream, F_OPEN))
333		return (0);
334
335	/*
336	 * Since fopen() doesn't return writable streams, the only valid
337	 * writable streams are stdout and stderr.
338	 */
339	if (stream != stdout && stream != stderr) {
340		errno = EBADF;
341		return (0);
342	}
343
344	prom_writestr(ptr, size * nitems);
345	return (nitems);
346}
347
348/*ARGSUSED*/
349int
350setvbuf(FILE *stream, char *buf, int type, size_t size)
351{
352	if (!fcheck(stream, F_OPEN))
353		return (-1);
354
355	/* Currently a nop, probably always will be. */
356	return (0);
357}
358