• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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