utils.cpp (3819B)
1 // Copyright (c) 2020 Morel BĂ©renger 2 // 3 // This software is provided 'as-is', without any express or implied 4 // warranty. In no event will the authors be held liable for any damages 5 // arising from the use of this software. 6 // 7 // Permission is granted to anyone to use this software for any purpose, 8 // including commercial applications, and to alter it and redistribute it 9 // freely, subject to the following restrictions: 10 // 11 // 1. The origin of this software must not be misrepresented; you must not 12 // claim that you wrote the original software. If you use this software 13 // in a product, an acknowledgment in the product documentation would be 14 // appreciated but is not required. 15 // 2. Altered source versions must be plainly marked as such, and must not be 16 // misrepresented as being the original software. 17 // 3. This notice may not be removed or altered from any source distribution. 18 19 #include <stdio.h> 20 #include <errno.h> 21 #include <limits.h> 22 #include <assert.h> 23 #include <string.h> 24 25 #include <iterator> 26 #include <algorithm> 27 28 #include "utils.hpp" 29 30 #include <sys/ioctl.h> 31 #include <termios.h> 32 33 //would probably be better without that many ifs 34 int esc_fputs( const char *s, FILE *stream ) 35 { 36 assert( s && stream && "nullpointer provided to esc_fputs" ); 37 if( !( s && stream ) ) 38 { 39 errno = EINVAL; 40 return EOF; 41 } 42 43 const char *str_end = s + strlen( s ); 44 const char *chunk_start = s; 45 const char *chunk_end; 46 char bslash[2] = { '\\', '\\' }; 47 char tohex [4] = { '\\', 'x', 0, 0 }; 48 char ch; 49 char const* buf; 50 size_t sz; 51 52 do 53 { 54 chunk_end = std::find_if( chunk_start, str_end, []( const char c ){ return c < 0x20 || c == '\\'; } ); 55 assert( chunk_end - chunk_start >= 0 ); 56 sz = static_cast<size_t>( chunk_end - chunk_start ); 57 if( sz != 0 && !fwrite( chunk_start, sz, 1, stream ) ) 58 { 59 return INT_MAX; 60 } 61 if( chunk_end != str_end ) 62 { 63 ch = *chunk_end; 64 buf = bslash; 65 sz = sizeof( bslash ); 66 if( ch != '\\' ) 67 { 68 buf = tohex; 69 sz = sizeof( tohex ); 70 //quick'n dirty ascii to hex convertion for values < 0x20 (space) 71 tohex[2] = '0' + ( ch >> 4 ); 72 ch &= 0x0F; 73 tohex[3] = ch + '0' + ( ch > 9 ? 'A' - ( '9' + 1 ) : 0 ); 74 } 75 if( !fwrite( buf, sz, 1, stream ) ) 76 { 77 return INT_MAX; 78 } 79 ++chunk_end; 80 } 81 chunk_start = chunk_end; 82 } while( chunk_start != str_end ); 83 return 0; 84 } 85 86 size_t ungets( char const* const str, size_t str_sz ) 87 { 88 // reverse push, since otherwise one would start with EOF 89 for( size_t i = str_sz; i; --i ) 90 { 91 if( EOF == ungetc( str[i-1], stdin ) ) 92 { 93 return i; 94 } 95 } 96 return 0; 97 } 98 99 bool term_ch_size( uint16_t *w, uint16_t *h, int fd ) 100 { 101 struct winsize wsz; 102 if( -1 == ioctl( fd, TIOCGWINSZ, &wsz ) ) 103 { 104 return true; 105 } 106 *w = wsz.ws_col; 107 *h = wsz.ws_row; 108 return false; 109 } 110 111 bool indent_txt( char const* txt, char const* end, uint8_t level, uint16_t max_width, uint8_t tab_width, FILE* target ) 112 { 113 uint16_t indent_size = tab_width * level; 114 //bad args 115 if( !txt || !target ) 116 { 117 return true; 118 } 119 //overflow 120 if( indent_size < tab_width || indent_size < level ) 121 { 122 return true; 123 } 124 //indent wider than screen 125 if( indent_size > max_width ) 126 { 127 return true; 128 } 129 max_width -= indent_size; 130 assert( max_width > 0 ); 131 132 uint16_t pos = 0; 133 uint16_t last_blank = 0; 134 char const* ptr = txt; 135 for( ; ptr != end; ++ptr, ++pos ) 136 { 137 if( isblank( *ptr ) ) 138 { 139 last_blank = pos; 140 } 141 142 if( *ptr == '\n' || pos == max_width ) 143 { 144 if( *ptr != '\n' && last_blank != 0 ) 145 { 146 assert( pos >= last_blank ); 147 ptr -= pos - last_blank; 148 pos = last_blank; 149 } 150 for( uint8_t l = 0; l < level; ++l ) 151 { 152 fputc( '\t', target ); 153 } 154 fprintf( target, "%.*s", pos, txt ); 155 last_blank = 0; 156 pos = 0; 157 txt = ptr + 1; 158 if( *ptr != '\n' ) 159 { 160 fputc( '\n', target ); 161 } 162 } 163 } 164 return false; 165 }