aboutsummaryrefslogtreecommitdiff
path: root/lib/linestack.c
blob: 241a437d1035c7ffb075d93c84790e2933136855 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include "toys.h"

// Insert one stack into another before position in old stack.
// (Does not copy contents of strings, just shuffles index array contents.)
void linestack_addstack(struct linestack **lls, struct linestack *throw,
  long pos)
{
  struct linestack *catch = *lls;

  if (CFG_TOYBOX_DEBUG)
    if (pos > catch->len) error_exit("linestack_addstack past end.");

  // Make a hole, allocating more space if necessary.
  if (catch->len+throw->len >= catch->max) {
    // New size rounded up to next multiple of 64, allocate and copy start.
    catch->max = ((catch->len+throw->len)|63)+1;
    *lls = xmalloc(sizeof(struct linestack)+catch->max*sizeof(struct ptr_len));
    memcpy(*lls, catch, sizeof(struct linestack)+pos*sizeof(struct ptr_len));
  }

  // Copy end (into new allocation if necessary)
  if (pos != catch->len)
    memmove((*lls)->idx+pos+throw->len, catch->idx+pos,
      (catch->len-pos)*sizeof(struct ptr_len));

  // Cleanup if we had to realloc.
  if (catch != *lls) {
    free(catch);
    catch = *lls;
  }

  memcpy(catch->idx+pos, throw->idx, throw->len*sizeof(struct ptr_len));
  catch->len += throw->len;
}

void linestack_insert(struct linestack **lls, long pos, char *line, long len)
{
  // alloca() was in 32V and Turbo C for DOS, but isn't in posix or c99.
  // I'm not thrashing the heap for this, but this should work even if
  // a broken compiler adds gratuitous padding.
  struct {
    struct linestack ls;
    struct ptr_len pl;
  } ls;

  ls.ls.len = ls.ls.max = 1;
  ls.ls.idx[0].ptr = line;
  ls.ls.idx[0].len = len;
  linestack_addstack(lls, &ls.ls, pos);
}

void linestack_append(struct linestack **lls, char *line)
{
  linestack_insert(lls, (*lls)->len, line, strlen(line));
}

struct linestack *linestack_load(char *name)
{
  FILE *fp = fopen(name, "r");
  struct linestack *ls;

  if (!fp) return 0;

  ls = xzalloc(sizeof(struct linestack));

  for (;;) {
    char *line = 0;
    ssize_t len;

    if ((len = getline(&line, (void *)&len, fp))<1) break;
    if (line[len-1]=='\n') len--;
    linestack_insert(&ls, ls->len, line, len);
  }
  fclose(fp);

  return ls;
}

// Show width many columns, negative means from right edge.
// If out=0 just measure
// if escout, send it unprintable chars, returns columns output or -1 for
// standard escape: ^X if <32, <XX> if invliad UTF8, U+XXXX if UTF8 !iswprint()
// Returns width in columns, moves *str to end of data consumed.
int crunch_str(char **str, int width, FILE *out,
  int (*escout)(FILE *out, int cols, char **buf))
{
  int columns = 0, col, bytes;
  char *start, *end;

  for (end = start = *str; *end;) {
    wchar_t wc = *end;

    bytes = 0;
    if (*end >= ' ' && (bytes = mbrtowc(&wc, end, 99,0))>0
        && (col = wcwidth(wc))>=0)
    {
      if (width-columns<col) break;
      if (out) fwrite(end, bytes, 1, out);
    } else if (!escout || 0>(col = escout(out, width-columns, &end))) {
      char buf[32];

      tty_esc("7m");
      if (*end < ' ') {
        bytes = 1;
        sprintf(buf, "^%c", '@'+*end);
      } else if (bytes<1) {
        bytes = 1;
        sprintf(buf, "<%02X>", *end);
      } else sprintf(buf, "U+%04X", wc);
      col = strlen(buf);
      if (width-columns<col) buf[col = width-columns] = 0;
      if (out) fputs(buf, out);
      tty_esc("27m");
    } else continue;
    columns += col;
    end += bytes;
  }
  *str = end;

  return columns;
}

// Write width chars at start of string to strdout with standard escapes
// Returns length in columns so caller can pad it out with spaces.
int draw_str(char *start, int width)
{
  return crunch_str(&start, width, stdout, 0);
}

// Return utf8 columns
int utf8len(char *str)
{
  return crunch_str(&str, INT_MAX, 0, 0);
}

// Return bytes used by (up to) this many columns
int utf8skip(char *str, int width)
{
  char *s = str;

  crunch_str(&s, width, 0, 0);

  return s-str;
}

// Print utf8 to stdout with standard escapes,trimmed to width and padded
// out to padto. If padto<0 left justify. Returns columns printed
int draw_trim(char *str, int padto, int width)
{
  int apad = abs(padto), len = utf8len(str);

  if (padto<0 && len>width) str += utf8skip(str, len-width);
  if (len>width) len = width;

  // Left pad if right justified 
  if (padto>0 && apad>len) printf("%*s", apad-len, "");
  crunch_str(&str, len, stdout, 0);
  if (padto<0 && apad>len) printf("%*s", apad-len, "");

  return (apad > len) ? apad : len;
}