summaryrefslogblamecommitdiff
path: root/src/user/lib/libc/std/stdio.c
blob: be65505e548da84db70aef55870fcc0f2e3ac325 (plain) (tree)
1
2
3
4
5
6
                  
                   
                  
                        
                  
 

























































































































































































































                                                                                                      

 
                                      
                   
              
                             
                                        
                   
                 
 
 

                                                 

 
                                               
                   
              
                             
                                    
                   
                 

 
                                                    



                                         
                      
                                   

                                               
 
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <tce/syscall.h>
#include <ctype.h>

FILE term;


// GENERAL FILE ROUTINES

FILE *fopen(const char *path, const char *mode) {
	int m = 0;
	if (strchr(mode, 'r')) m |= FM_READ;
	if (strchr(mode, 'w')) m |= FM_WRITE;
	if (strchr(mode, 't')) m |= FM_TRUNC;
	if (strchr(mode, 'c')) m |= FM_CREATE;

	FILE *ret = malloc(sizeof(FILE));
	if (!ret) return NULL;

	ret->fd = open(path, m);
	if (ret->fd < 0) {
		errno = ret->fd;
		free(ret);
		return NULL;
	}
	__tce_libc_fsetup(ret);

	return ret;
}

void __tce_libc_fsetup(FILE *f) {
	statf(f->fd, &f->info);
	f->pos = 0;

	if (f->info.type & FT_TERMINAL) {
		f->pos = -1;

		f->tib_begin = malloc(TERM_INPUT_BUFFER_SIZE);
		f->tib_end = f->tib_begin + TERM_INPUT_BUFFER_SIZE;
		f->tib_u_begin = f->tib_begin;
		f->tib_u_end = f->tib_u_begin;
		f->term_echo = 1;
	}
}

int fgetc(FILE *f) {
	if ((f->info.type & FT_TERMINAL) && f->tib_begin) {
		if (f->tib_u_end == f->tib_u_begin) {
			f->tib_u_begin = f->tib_u_end = f->tib_begin;
			// read one line into buffer
			int cont = 1;
			while (cont) {
				int k;
				int n = read(f->fd, 0, f->tib_end - f->tib_u_end, f->tib_u_end);
				for (k = 0; k < n; k++) {
					if (f->term_echo) fprintf(f, "%c", f->tib_u_end[k]);
					if (f->tib_u_end[k] == '\n' || f->tib_u_end[k] == 4) cont = 0;
				}
				f->tib_u_end += k;
				if (f->tib_u_end == f->tib_end) cont = 0;
			}

		}

		if (f->tib_u_end > f->tib_u_begin) {
			char c = *f->tib_u_begin;
			f->tib_u_begin++;
			if (f->tib_u_begin == f->tib_end) {
				f->tib_u_begin = f->tib_u_end = f->tib_begin;	// empty buffer
			}
			return c;
		}
	} else {
		char c;
		int r = read(f->fd, (f->pos == -1 ? 0 : f->pos), 1, &c);
		if (r == 1) {
			if (f->pos != -1) f->pos++;
			return c;
		}
	}
	return EOF;
}

int ungetc(int c, FILE *f) {
	if (f->info.type & FT_TERMINAL && f->tib_begin) {
		if (f->tib_u_begin > f->tib_begin) {
			// buffer contains data which is partially read already
			f->tib_u_begin--;
			*(f->tib_u_begin) = c;
		} else if (f->tib_u_end == f->tib_u_begin) {
			// buffer is totally empty
			*(f->tib_u_end) = c;
			f->tib_u_end++;
		} else {
			return EOF;		// no space in buffer.
		}
	} else if (f->pos >0) {
		f->pos--;
	} else {
		return EOF;
	}
	return c;
}

size_t fread(void *ptr, size_t size, size_t nmemb, FILE* f) {
	if (size * nmemb == 0) return 0;

	int r = read(f->fd, (f->pos == -1 ? 0 : f->pos), size * nmemb, ptr);
	if (r > 0) {
		if (f->pos != -1) f->pos += r;
		return r;
	} else {
		errno = r;
		return 0;
	}
}

size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *f) {
	if (size * nmemb == 0) return 0;
	int r = write(f->fd, (f->pos == -1 ? 0 : f->pos), size * nmemb, ptr);
	if (r > 0) {
		if (f->pos != -1) f->pos += r;
		return r;
	} else {
		errno = r;
		return 0;
	}
}

size_t fseek(FILE *f, long offset, int whence) {
	// Update info
	statf(f->fd, &f->info);

	if (!(f->info.type & FT_FILE)) {
		errno = EBADF;
		return -1;
	}

	if (whence == SEEK_CUR) {
		offset += f->pos;
	}
	if (whence == SEEK_END) {
		offset = f->info.size - offset;
	}

	if (offset > f->info.size || offset < 0) {
		errno = EINVAL;
		return -1;
	}
	f->pos = offset;

	return 0;
}

long ftell(FILE *f) {
	return f->pos;
}

void fclose(FILE *f) {
	if (f->tib_begin) free(f->tib_begin);
	close(f->fd);
	free(f);
}

// COMFY INPUT FUNCTIONS

int getchar() {
	return fgetc(&term);
}

int scanf(const char *format, ...) {
	// rudimentary implementation
	int r = 0; 

	va_list ap;
	va_start(ap, format);

	while (*format) {
		if (*format == '%') {
			format++;
			if (!(*format)) break;

			if (*format == 'd' || *format == 'i') {
				int c = fgetc(&term);

				int *ret = va_arg(ap, int*);
				*ret = 0;

				while (isdigit(c)) {
					(*ret) *= 10;
					(*ret) += (c - '0');
					r++;
					c = fgetc(&term);
				}
				ungetc(c, &term);
			} else if (*format == 'c') {
				char *c = va_arg(ap, char*);
				*c = fgetc(&term);
				r++;
			}
			format++;
		} else {
			int c = fgetc(&term);
			if (c == *format) {
				r++;
				format++;
			} else {
				ungetc(c, &term);
				break;
			}
		}
	}

	va_end(ap);
	return r;
}

// COMFY OUTPUT FUNCTIONS

int print(const char *s) {
	fprint(&term, s);
	return strlen(s);
}

int printf(const char *format,  ...) {
	va_list ap;
	int l;
	va_start(ap, format);
	l = vfprintf(&term, format, ap);
	va_end(ap);
	return l;
}

int fprint(FILE *f, const char *s) {
	return fwrite((void*)s, strlen(s), 1, f);
}

int fprintf(FILE *f, const char* format, ...) {
	va_list ap;
	int l;
	va_start(ap, format);
	l = vfprintf(f, format, ap);
	va_end(ap);
	return l;
}

int vfprintf(FILE *f, const char *fmt, va_list ap) {
	va_list ap2;
	va_copy(ap2, ap);
	int l = printf_str_len(fmt, ap2);
	va_end(ap2);
	char buf[l+1];
	l = vsprintf(buf, fmt, ap);
	if (l > 0) return fwrite(buf, l, 1, f);
	return 0;
}