1 #include "toys.h" 2 3 // A linestack is an array of struct ptr_len. 4 5 // Insert one stack into another before position in old stack. 6 // (Does not copy contents of strings, just shuffles index array contents.) linestack_addstack(struct linestack ** lls,struct linestack * throw,long pos)7 void linestack_addstack(struct linestack **lls, struct linestack *throw, 8 long pos) 9 { 10 struct linestack *catch = *lls; 11 12 if (CFG_TOYBOX_DEBUG) 13 if (pos > catch->len) error_exit("linestack_addstack past end."); 14 15 // Make a hole, allocating more space if necessary. 16 if (catch->len+throw->len >= catch->max) { 17 // New size rounded up to next multiple of 64, allocate and copy start. 18 catch->max = ((catch->len+throw->len)|63)+1; 19 *lls = xmalloc(sizeof(struct linestack)+catch->max*sizeof(struct ptr_len)); 20 memcpy(*lls, catch, sizeof(struct linestack)+pos*sizeof(struct ptr_len)); 21 } 22 23 // Copy end (into new allocation if necessary) 24 if (pos != catch->len) 25 memmove((*lls)->idx+pos+throw->len, catch->idx+pos, 26 (catch->len-pos)*sizeof(struct ptr_len)); 27 28 // Cleanup if we had to realloc. 29 if (catch != *lls) { 30 free(catch); 31 catch = *lls; 32 } 33 34 // Copy new chunk we made space for 35 memcpy(catch->idx+pos, throw->idx, throw->len*sizeof(struct ptr_len)); 36 catch->len += throw->len; 37 } 38 39 // Insert one line/len into a linestack at pos linestack_insert(struct linestack ** lls,long pos,char * line,long len)40 void linestack_insert(struct linestack **lls, long pos, char *line, long len) 41 { 42 // alloca() was in 32V and Turbo C for DOS, but isn't in posix or c99. 43 // This allocates enough memory for the linestack to have one ptr_len. 44 // (Even if a compiler adds gratuitous padidng that just makes it bigger.) 45 struct { 46 struct linestack ls; 47 struct ptr_len pl; 48 } ls; 49 50 ls.ls.len = ls.ls.max = 1; 51 ls.ls.idx[0].ptr = line; 52 ls.ls.idx[0].len = len; 53 linestack_addstack(lls, &ls.ls, pos); 54 } 55 linestack_append(struct linestack ** lls,char * line)56 void linestack_append(struct linestack **lls, char *line) 57 { 58 linestack_insert(lls, (*lls)->len, line, strlen(line)); 59 } 60 linestack_load(char * name)61 struct linestack *linestack_load(char *name) 62 { 63 FILE *fp = fopen(name, "r"); 64 struct linestack *ls; 65 66 if (!fp) return 0; 67 68 ls = xzalloc(sizeof(struct linestack)); 69 70 for (;;) { 71 char *line = 0; 72 ssize_t len; 73 74 if ((len = getline(&line, (void *)&len, fp))<1) break; 75 if (line[len-1]=='\n') len--; 76 linestack_insert(&ls, ls->len, line, len); 77 } 78 fclose(fp); 79 80 return ls; 81 } 82 83 // Show width many columns, negative means from right edge, out=0 just measure 84 // if escout, send it unprintable chars, otherwise pass through raw data. 85 // Returns width in columns, moves *str to end of data consumed. crunch_str(char ** str,int width,FILE * out,char * escmore,int (* escout)(FILE * out,int cols,int wc))86 int crunch_str(char **str, int width, FILE *out, char *escmore, 87 int (*escout)(FILE *out, int cols, int wc)) 88 { 89 int columns = 0, col, bytes; 90 char *start, *end; 91 92 for (end = start = *str; *end; columns += col, end += bytes) { 93 wchar_t wc; 94 95 if ((bytes = mbrtowc(&wc, end, MB_CUR_MAX, 0))>0 && (col = wcwidth(wc))>=0) 96 { 97 if (!escmore || wc>255 || !strchr(escmore, wc)) { 98 if (width-columns<col) break; 99 if (out) fwrite(end, bytes, 1, out); 100 101 continue; 102 } 103 } 104 105 if (bytes<1) { 106 bytes = 1; 107 wc = *end; 108 } 109 col = width-columns; 110 if (col<1) break; 111 if (escout) col = escout(out, col, wc); 112 else if (out) fwrite(end, bytes, 1, out); 113 } 114 *str = end; 115 116 return columns; 117 } 118 119 120 // standard escapes: ^X if <32, <XX> if invliad UTF8, U+XXXX if UTF8 !iswprint() crunch_escape(FILE * out,int cols,int wc)121 int crunch_escape(FILE *out, int cols, int wc) 122 { 123 char buf[8]; 124 int rc; 125 126 if (wc<' ') rc = sprintf(buf, "^%c", '@'+wc); 127 else if (wc<256) rc = sprintf(buf, "<%02X>", wc); 128 else rc = sprintf(buf, "U+%04X", wc); 129 130 if (rc > cols) buf[rc = cols] = 0; 131 if (out) fputs(buf, out); 132 133 return rc; 134 } 135 136 // Display "standard" escapes in reverse video. crunch_rev_escape(FILE * out,int cols,int wc)137 int crunch_rev_escape(FILE *out, int cols, int wc) 138 { 139 int rc; 140 141 tty_esc("7m"); 142 rc = crunch_escape(out, cols, wc); 143 tty_esc("27m"); 144 145 return rc; 146 } 147 148 // Write width chars at start of string to strdout with standard escapes 149 // Returns length in columns so caller can pad it out with spaces. draw_str(char * start,int width)150 int draw_str(char *start, int width) 151 { 152 return crunch_str(&start, width, stdout, 0, crunch_rev_escape); 153 } 154 155 // Return utf8 columns utf8len(char * str)156 int utf8len(char *str) 157 { 158 return crunch_str(&str, INT_MAX, 0, 0, crunch_rev_escape); 159 } 160 161 // Return bytes used by (up to) this many columns utf8skip(char * str,int width)162 int utf8skip(char *str, int width) 163 { 164 char *s = str; 165 166 crunch_str(&s, width, 0, 0, crunch_rev_escape); 167 168 return s-str; 169 } 170 171 // Print utf8 to stdout with standard escapes, trimmed to width and padded 172 // out to padto. If padto<0 left justify. Returns columns printed draw_trim_esc(char * str,int padto,int width,char * escmore,int (* escout)(FILE * out,int cols,int wc))173 int draw_trim_esc(char *str, int padto, int width, char *escmore, 174 int (*escout)(FILE *out, int cols, int wc)) 175 { 176 int apad = abs(padto), len = utf8len(str); 177 178 if (padto>=0 && len>width) str += utf8skip(str, len-width); 179 if (len>width) len = width; 180 181 // Left pad if right justified 182 if (padto>0 && apad>len) printf("%*s", apad-len, ""); 183 crunch_str(&str, len, stdout, 0, crunch_rev_escape); 184 if (padto<0 && apad>len) printf("%*s", apad-len, ""); 185 186 return (apad > len) ? apad : len; 187 } 188 189 // draw_trim_esc() with default escape draw_trim(char * str,int padto,int width)190 int draw_trim(char *str, int padto, int width) 191 { 192 return draw_trim_esc(str, padto, width, 0, 0); 193 } 194