/* $Source$
 * $State$
 * $Revision$
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "libsys.h"

ssize_t read(int fd, void* buffer, size_t count)
{
	char *p, *q;
	ssize_t r, tot;
	size_t left;
	int eof = 0;
	bool text = _sys_getmode(fd) == O_TEXT;

	if (_sys_iseof(fd))
		return 0;

	/*
	 * If the file descriptor is an O_TEXT fd, read from it, and then
	 * remove CRs from the input.  After removing CRs, if the buffer is
	 * not filled and the fd still has bytes left to read, then keep
	 * reading, and keep removing CRs.
	 *
	 * Also handle end-of-file indicators (^Z).  If we see one, move the
	 * file pointer to just before the ^Z.  Also set an internal flag
	 * for the fd so that we do not try to read any further (until e.g. 
	 * a seek happens).
	 *
	 * If a low-level read returns an error (-1) before anything has
	 * actually been read, we can pass the error indication (-1) up to
	 * the caller.
	 */
	p = buffer;
	tot = 0;

	do
	{
		r = _sys_rawread(fd, p, count - (p - (char *)buffer));
		if (r <= 0)
			return tot ? tot : r;

		if (text)
		{
			q = memchr(p, 0x1a, (size_t)r);
			if (q)
			{
				_sys_rawlseek(fd, (off_t)(q - p) - r, SEEK_CUR);
				r = q - p;
				eof = 1;
				_sys_seteof(fd, 1);
			}

			q = memchr(p, '\r', (size_t)r);
			if (!q)
				return tot + r;

			tot += q - p;
			left = r - (q + 1 - p);
			p = q;
			++q;

			while (left)
			{
				char c = *q++;
				if (c != '\r')
				{
					*p++ = c;
					++tot;
				}
				--left;
			}
		}
	} while (tot < count && !eof && _sys_isreadyr(fd));

	return tot;
}
