/* * stdio.c - User level library implementation. * * Author Alain Greiner (2016,2017,2018) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////////////// // stdio library global variables //////////////////////////////////////////////////////////////////////////////////////// // This user space array registers all FILE descriptors that can be simultaneously // open by a given process. The structure FILE is defined in the file. FILE open_file_array[MAX_OPEN_FILE_PER_PROCESS]; //////////////////////////////////////////////////////////////////////////////////////// // stdio library functions //////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// int rename( const char * old, const char * new ) { return hal_user_syscall( SYS_RENAME, (reg_t)old, (reg_t)new, 0, 0 ); } //////////////////////////////////////////////////////////////////////////////////////// // This static function analyses the and the to build a formated // string in the buffer defined by and . // It does NOT add a terminating NUL character in the . // If success, it returns the number of bytes actually copied in the string buffer. // It returns -1 in case of illegal format, or if the formated string exceeds the // the length argument. //////////////////////////////////////////////////////////////////////////////////////// static int xprintf( char * string, int length, const char * format, va_list * args ) { int ps = 0; // index in the string buffer #define TO_STREAM(x) do { string[ps] = (x); ps++; if(ps==length) return -1; } while(0); xprintf_text: while ( *format != 0 ) { if (*format == '%') // copy argument to string { format++; goto xprintf_arguments; } else // copy one char to string { TO_STREAM( *format ); format++; } } return ps; xprintf_arguments: { char buf[30]; // buffer to store the string for one number char * pbuf; // pointer on first char to display unsigned int len = 0; // number of char to display static const char HexaTab[] = "0123456789ABCDEF"; unsigned int i; // Ignore fields width and precision for ( ; (*format >= '0' && *format <= '9') || (*format == '.') ; format++ ); switch (*format) { case ('c'): // char conversion { int val = va_arg( *args, int ); buf[0] = val; pbuf = buf; len = 1; break; } case ('d'): // decimal signed integer { int val = va_arg( *args, int ); if (val < 0) { TO_STREAM( '-' ); val = -val; } for(i = 0; i < 10; i++) { buf[9 - i] = HexaTab[val % 10]; if (!(val /= 10)) break; } len = i + 1; pbuf = &buf[9 - i]; break; } case ('u'): // decimal unsigned integer { unsigned int val = va_arg( *args, unsigned int ); for(i = 0; i < 10; i++) { buf[9 - i] = HexaTab[val % 10]; if (!(val /= 10)) break; } len = i + 1; pbuf = &buf[9 - i]; break; } case ('x'): // up to 8 digits hexa after "0x" { unsigned int val = va_arg( *args , unsigned int ); TO_STREAM( '0' ); TO_STREAM( 'x' ); for(i = 0 ; i < 8 ; i++) { buf[7 - i] = HexaTab[val & 0xF]; val = val >> 4; if( val == 0) break; } len = i + 1; pbuf = &buf[7 - i]; break; } case ('X'): // exactly 8 digits hexa after "0x" { unsigned int val = va_arg( *args , unsigned int ); TO_STREAM( '0' ); TO_STREAM( 'x' ); for(i = 0 ; i < 8 ; i++) { buf[7 - i] = (val != 0) ? HexaTab[val & 0xF] : '0'; val = val >> 4; } len = 8; pbuf = &buf[0]; break; } case ('l'): // up to 16 digits hexa after "0x" { unsigned long long val = ((unsigned long long)va_arg( *args, unsigned int)) | ((unsigned long long)va_arg( *args, unsigned int) << 32); TO_STREAM( '0' ); TO_STREAM( 'x' ); for(i = 0 ; i < 16 ; i++) { buf[15 - i] = HexaTab[val & 0xF]; val = val >> 4; if( val == 0) break; } len = i + 1; pbuf = &buf[15 - i]; break; } case ('L'): // exactly 16 digits hexa after "0x" { unsigned long long val = ((unsigned long long)va_arg( *args, unsigned int)) | ((unsigned long long)va_arg( *args, unsigned int) << 32); TO_STREAM( '0' ); TO_STREAM( 'x' ); for(i = 0 ; i < 16 ; i++) { buf[15 - i] = (val != 0) ? HexaTab[val & 0xF] : '0'; val = val >> 4; } len = 16; pbuf = &buf[0]; break; } case ('s'): /* string */ { char * str = va_arg( *args , char* ); while (str[len]) { len++; } pbuf = str; break; } case ('f'): // IEEE754 64 bits // integer part : up to 10 decimal digits // decimal part : 9 decimal digits { union { double d; unsigned long long ull; } val; val.d = va_arg( *args, double ); unsigned long long mantisse; mantisse = val.ull & 0xFFFFFFFFFFFFFULL; // mantisse unsigned int exp; exp = (unsigned int)((val.ull & 0x7FF0000000000000ULL) >> 52); // exp if (exp == 0x7FF) // special values { if (mantisse & 0xFFFFFFFFFFFFFULL) // Not a Number { buf[0] = 'N'; buf[1] = 'a'; buf[2] = 'N'; len = 3; pbuf = buf; } else // infinite { // inf buf[0] = (val.ull & 0x8000000000000000ULL) ? '-' : '+'; buf[1] = 'i'; buf[2] = 'n'; buf[3] = 'f'; len = 4; pbuf = buf; } break; } // display sign & analyse overflow unsigned int overflow = 0; if (val.ull & 0x8000000000000000ULL) // negative { TO_STREAM( '-' ); val.d = val.d * -1; if( val.d < -9999999999.0) overflow = 1; } else // positive { TO_STREAM( '+' ); if( val.d > 9999999999.0) overflow = 1; } // check overflow caused by the 10.9 format if ( overflow ) { buf[0] = 'o'; buf[1] = 'v'; buf[2] = 'r'; len = 3; pbuf = buf; break; } // compute integer & decimal parts unsigned int intp; // integer part unsigned int decp; // decimal part intp = (unsigned int)val.d; val.d -= (double)intp; decp = (unsigned int)(val.d * 1000000000); // display decimal value in 10.9 format for(i = 0; i < 10; i++) { buf[9 - i] = HexaTab[intp % 10]; if (!(intp /= 10)) break; } pbuf = &buf[9 - i]; len = i+11; buf[10] = '.'; for(i = 0; i < 9; i++) { buf[19 - i] = HexaTab[decp % 10]; decp /= 10; } break; } default: // unsupported argument type { return -1; } } // end switch on argument type format++; // copy argument sub-string to the string buffer for( i = 0 ; i < len ; i++ ) { TO_STREAM( pbuf[i] ); } goto xprintf_text; } } // end xprintf() ////////////////////////////////////// int printf( const char * format, ... ) { char string[4096]; va_list args; int count; va_start( args, format ); count = xprintf( string , 4095 , format , &args ); va_end( args ); if ( count < 0 ) { display_string( "printf : xprintf failure" ); return -1; } else { string[count] = 0; return write( 1 , &string , count ); } } // end printf() /////////////////// int getchar( void ) { char byte; if ( read( 0 , &byte , 1 ) != 1 ) return 0; else return (int)byte; } //////////////////// int putchar( int c ) { char byte = (char)c; if( write( 1 , &byte , 1 ) != 1 ) return 0; else return c; } /////////////////////////////////////// int snprintf( char * string, unsigned int length, const char * format, ... ) { va_list args; int count; va_start( args, format ); count = xprintf( string , (int)length , format , &args ); va_end( args ); if( (count < 0) || (count == (int)length) ) // failure { return -1; } else // success { // add NUL character string[count] = 0; return count; } } // end snprintf() //////////////////////////////////// FILE * fopen( const char * pathname, const char * mode ) { //TODO handle the "mode" argument if( mode != NULL ) { printf("\n[%s] error : the mode argument must be NULL\n", __FUNCTION__ ); return NULL; } // get a file descriptor from kernel int fd = open( pathname, O_CREAT | O_RDWR, 0 ); if( fd < 0 ) { printf("\n[%s] error : file <%s> not found\n", __FUNCTION__ , pathname ); return NULL; } if( fd > MAX_OPEN_FILE_PER_PROCESS ) { printf("\n[%s] error : not enough space for file <%s>\n", __FUNCTION__ , pathname ); return NULL; } // register stream in open_file_array[] open_file_array[fd].fd = fd; open_file_array[fd].key = VALID_OPEN_FILE; return &open_file_array[fd]; } // end fopen() /////////////////////////// int fclose( FILE * stream ) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return EOF; // get file descriptor from stream pointer int fd = stream->fd; // remove stream from user open_file_array[] open_file_array[fd].key = 0; // close the kernel file descriptor if( close( fd ) ) { printf("\n[%s] error : cannot close file %d\n", __FUNCTION__ , fd ); return -1; } return 0; } // end fclose() ////////////////////////// int fgetc( FILE * stream ) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return EOF; // get file descriptor from stream pointer int fd = stream->fd; char byte; if ( read( fd , &byte , 1 ) != 1 ) return 0; else return (int)byte; } ///////////////// int fputc( int c, FILE * stream) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return EOF; // get file descriptor from stream pointer int fd = stream->fd; char byte = (char)c; if( write( fd , &byte , 1 ) != 1 ) return 0; else return c; } ////////////////////////////////////////// unsigned int fread( void * buffer, unsigned int size, unsigned int nitems, FILE * stream ) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return EOF; // get file descriptor from stream pointer int fd = stream->fd; // compute nbytes int nbytes = size * nitems; if( hal_user_syscall( SYS_READ, (reg_t)fd, (reg_t)buffer, (reg_t)nbytes, 0 ) == nbytes ) return nitems; else return 0; } // end fread() ////////////////////////////////////////// unsigned int fwrite( void * buffer, unsigned int size, unsigned int nitems, FILE * stream ) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return EOF; // get file descriptor from stream pointer int fd = stream->fd; // compute nbytes int nbytes = size * nitems; if( hal_user_syscall( SYS_WRITE, (reg_t)fd, (reg_t)buffer, (reg_t)nbytes, 0 ) == nbytes ) return nitems; else return 0; } // end fwrite() ////////////////////////////////////////// unsigned int fseek( FILE * stream, unsigned int offset, int whence ) { // check stream valid if( stream->key != VALID_OPEN_FILE ) return -1; // get file descriptor from stream pointer int fd = stream->fd; return( hal_user_syscall( SYS_LSEEK, (reg_t)fd, (reg_t)offset, (reg_t)whence, 0 ) ); } // end fseek() ///////////////////////////////// int fprintf( FILE * stream, const char * format, ... ) { char string[4096]; va_list args; int count; int writen; int fd; // check stream valid if( stream->key != VALID_OPEN_FILE ) { printf("\n[error in %s] stream %x non registered\n", __FUNCTION__, stream ); return -1; } va_start( args, format ); count = xprintf( string , 4095 , format , &args ); va_end( args ); // check format if ( count < 0 ) { printf("\n[error in %s] unsupported format %s\n", __FUNCTION__, format ); return -1; } // get file descriptor from file pointer fd = stream->fd; // set terminating NUL string[count] = 0; // copy string to file writen = write( fd , &string , count ); // check write if(writen != count ) { printf("\n[error in %s] cannot write to stream %s\n", __FUNCTION__, stream ); return -1; } return writen; } // end fprintf()