Click here to Skip to main content
15,886,046 members
Articles / Desktop Programming / MFC

Tiny C Runtime Library

Rate me:
Please Sign up or sign in to vote.
4.86/5 (60 votes)
25 Mar 20079 min read 332.2K   5.6K   123  
Reduce code bloat for those simple utility programs by using a streamlined C runtime - now with Unicode support!
// file.cpp

// file routine overrides

// 08/20/05 (mv)

#include <windows.h>
#include <stdio.h>
#include "libct.h"

/* FILE, as defined in stdio.h
struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;				Used to store HANDLE
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;
*/

//_flag values (not the ones used by the normal CRT
#define _FILE_TEXT		0x0001
#define _FILE_EOF		0x0002
#define _FILE_ERROR		0x0004

struct _FILE : public FILE
{
	void set_handle(HANDLE h) {_base = (char*)h;};
	HANDLE get_handle() const {return (HANDLE)_base;};
};

// used directly by the stdin, stdout, and stderr macros
_FILE __iob[3];
FILE *__iob_func() {return (FILE*)__iob;}

void _init_file()
{
	// STDIN
	__iob[0].set_handle(GetStdHandle(STD_INPUT_HANDLE));
	__iob[0]._flag = _FILE_TEXT;

	// STDOUT
	__iob[1].set_handle(GetStdHandle(STD_OUTPUT_HANDLE));
	__iob[1]._flag = _FILE_TEXT;

	// STDERR
	__iob[2].set_handle(GetStdHandle(STD_ERROR_HANDLE));
	__iob[2]._flag = _FILE_TEXT;
}


BEGIN_EXTERN_C

/*int _fileno(FILE *fp)
{
	return (int)fp;			// FIXME:  This doesn't work under Win64
}

HANDLE _get_osfhandle(int i)
{
	return (HANDLE)i;		// FIXME:  This doesn't work under Win64
}*/

FILE *fopen(const char *path, const char *attrs)
{
	DWORD access, disp;
	if (strchr(attrs, 'w'))
	{
		access = GENERIC_WRITE;
		disp = CREATE_ALWAYS;
	}
	else
	{
		access = GENERIC_READ;
		disp = OPEN_EXISTING;
	}

	HANDLE hFile = CreateFileA(path, access, 0, 0, disp, 0, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;

	_FILE *file = new _FILE;
	memset(file, 0, sizeof(_FILE));
	file->set_handle(hFile);

	if (strchr(attrs, 't'))
		file->_flag |= _FILE_TEXT;

	return file;
}

FILE *_wfopen(const wchar_t *path, const wchar_t *attrs)
{
	DWORD access, disp;
	if (wcschr(attrs, L'w'))
	{
		access = GENERIC_WRITE;
		disp = CREATE_ALWAYS;
	}
	else
	{
		access = GENERIC_READ;
		disp = OPEN_EXISTING;
	}

	HANDLE hFile = CreateFileW(path, access, 0, 0, disp, 0, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return 0;

	_FILE *file = new _FILE;
	memset(file, 0, sizeof(_FILE));
	file->set_handle(hFile);

	if (wcschr(attrs, L't'))
		file->_flag |= _FILE_TEXT;

	return file;
}


int fprintf(FILE *fp, const char *s, ...)
{
	va_list args;
	va_start(args, s);

	char bfr[1024];
	int len = wvsprintfA(bfr, s, args);
	va_end(args);

	fwrite(bfr, len+1, sizeof(char), fp);
	return len;
}

int fwprintf(FILE *fp, const wchar_t *s, ...)
{
	va_list args;
	va_start(args, s);

	wchar_t bfr[1024];
	int len = wvsprintfW(bfr, s, args);

	va_end(args);

	char ansibfr[1024];
	WideCharToMultiByte(CP_ACP, 0, bfr, -1, ansibfr, sizeof(ansibfr), 0, 0);

	fwrite(ansibfr, len+1, sizeof(char), fp);
	return len;
}


int fclose(FILE *fp)
{
	CloseHandle(((_FILE*)fp)->get_handle());
	delete fp;
	return 0;
}

int feof(FILE *fp)
{
	return (fp->_flag & _FILE_EOF) ? 1 : 0;
}

int fseek(FILE *str, long offset, int origin)
{
	DWORD meth = FILE_BEGIN;
	if (origin == SEEK_CUR)
		meth = FILE_CURRENT;
	else if (origin == SEEK_END)
		meth = FILE_END;
	SetFilePointer(((_FILE*)str)->get_handle(), offset, 0, meth);
	((_FILE*)str)->_flag &= ~_FILE_EOF;
	return 0;
}

long ftell(FILE *fp)
{
	return SetFilePointer(((_FILE*)fp)->get_handle(), 0, 0, FILE_CURRENT);
}

size_t fread(void *buffer, size_t size, size_t count, FILE *str)
{
	if (size*count == 0)
		return 0;
	if (feof(str))
		return 0;

	HANDLE hFile = ((_FILE*)str)->get_handle();
	int textMode = ((_FILE*)str)->_flag & _FILE_TEXT;

	char *src;
	if (textMode)
		src = (char*)malloc(size*count);
	else
		src = (char*)buffer;

	DWORD br;
	if (!ReadFile(hFile, src, (DWORD)(size*count), &br, 0))
		((_FILE*)str)->_flag |= _FILE_ERROR;
	else if (!br)		// nonzero return value and no bytes read = EOF
		((_FILE*)str)->_flag |= _FILE_EOF;

	if (!br)
		return 0;

	// Text-mode translation is always ANSI
	if (textMode)		// text mode: must translate CR -> LF
	{
		char *dst = (char*)buffer;
		for (DWORD i = 0; i < br; i++)
		{
			if (src[i] != '\r')
			{
				*dst++ = src[i];
				continue;
			}

			// If next char is LF -> convert CR to LF
			if (i+1 < br)
			{
				if (src[i+1] == '\n')
				{
					*dst++ = '\n';
					i++;
				}
				else
					*dst++ = src[i];
			}
			else if (br > 1)
			{
				// This is the hard part: must peek ahead one byte
				DWORD br2 = 0;
				char peekChar = 0;
				ReadFile(hFile, &peekChar, 1, &br2, 0);
				if (!br2)
					*dst++ = src[i];
				else if (peekChar == '\n')
					*dst++ = '\n';
				else
				{
					fseek(str, -1, SEEK_CUR);
					*dst++ = src[i];
				}
			}
			else
				*dst++ = src[i];
		}

		free(src);
	}

	return br/size;
}

size_t fwrite(const void *buffer, size_t size, size_t count, FILE *str)
{
	DWORD bw = 0, bw2 = 0;

	if (size*count == 0)
		return 0;

	HANDLE hFile = ((_FILE*)str)->get_handle();
	int textMode = ((_FILE*)str)->_flag & _FILE_TEXT;

	// Text-mode translation is always ANSI!
	if (textMode)			// text mode -> translate LF -> CRLF
	{
		const char *src = (const char*)buffer;
		size_t startpos = 0, i = 0;
		for (i = 0; i < size*count; i++)
		{
			if (src[i] != '\n')
				continue;
			if (i > 0 && src[i-1] == '\r')		// don't translate CRLF
				continue;

			if (i > startpos)
			{
				WriteFile(hFile, &src[startpos], i-startpos, &bw2, 0);
				bw += bw2;
			}

			const char *crlf = "\r\n";
			WriteFile(hFile, crlf, 2, &bw2, 0);
			bw++;		// one '\n' written

			startpos = i+1;
		}

		if (i > startpos)
		{
			WriteFile(hFile, &src[startpos], i-startpos, &bw2, 0);
			bw += bw2;
		}
	}
	else
		WriteFile(hFile, buffer, (DWORD)(size*count), &bw, 0);
	return bw/size;
}

char *fgets(char *str, int n, FILE *s)
{
	if (feof(s))
		return 0;

	int i;
	for (i = 0; i < n-1; i++)
	{
		if (!fread(&str[i], 1, sizeof(char), s))
			break;
		if (str[i] == '\r')
		{
			i--;
			continue;
		}
		if (str[i] == '\n')
		{
			i++;
			break;
		}
	}

	str[i] = 0;
	return str;
}

wchar_t *fgetws(wchar_t *str, int n, FILE *s)
{
	// Text-mode fgetws converts MBCS->Unicode
	if (((_FILE*)str)->_flag & _FILE_TEXT)
	{
		char *bfr = (char*)malloc(n);
		fgets(bfr, n, s);
		MultiByteToWideChar(CP_ACP, 0, bfr, -1, str, n);
		free(bfr);
		return str;
	}

	// Binary fgetws reads as Unicode

	if (feof(s))
		return 0;

	int i;
	for (i = 0; i < n-1; i++)
	{
		if (!fread(&str[i], 1, sizeof(wchar_t), s))
			break;
		if (str[i] == L'\r')
		{
			i--;
			continue;	// does i++
		}
		if (str[i] == L'\n')
		{
			i++;
			break;
		}
	}

	str[i] = 0;
	return str;
}


int fgetc(FILE *s)
{
	if (s == 0 || feof(s))
		return EOF;

	char c;
	fread(&c, 1, sizeof(char), s);

	return (int)c;
}

wint_t fgetwc(FILE *s)
{
	if (s == 0 || feof(s))
		return (wint_t)EOF;

	// text-mode fgetwc reads and converts MBCS
	if (((_FILE*)s)->_flag & _FILE_TEXT)
	{
		char ch = (char)fgetc(s);
		wint_t wch;
		MultiByteToWideChar(CP_ACP, 0, &ch, 1, (LPWSTR)&wch, 1);
		return wch;
	}

	// binary fgetwc reads unicode

	wint_t c;
	fread(&c, 1, sizeof(wint_t), s);

	return c;
}


END_EXTERN_C

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Mike_V is currently a student at UCLA.

After a few years on the Dark Side, he reformed and now chants "Death to VB." His computer-related interests include C++, C#, and ASP.NET (in C#, of course). He writes operating systems in C++ and assembler as a hobby.

Comments and Discussions