aboutsummaryrefslogtreecommitdiff
path: root/lib/linestack.c
blob: 83b0e276f2f0db294ca8c7c256cbeff54e517ad8 (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
#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);
}

// Write width chars at end of string to stdout with standard escapes
int draw_rstr(char *start, int width)
{
  char *s = start;
  int len = crunch_str(&s, INT_MAX, 0, 0);

  s = start;
  if (len > width) crunch_str(&s, len-width, 0, 0);
  return crunch_str(&s, width, stdout, 0);
}