#include "common.h" #include "strutl.h" #include "question.h" #include #include #include #include #include #include #ifdef HAVE_LIBTEXTWRAP #include #endif #include #include "debian-installer.h" /* Structure used to sort translated lists */ typedef struct { char *string; int index; } sort_str_t; int strcountcmp(const char *s1, const char *e1, const char *s2, const char *e2) { for (; s1 != e1 && s2 != e2 && *s1 == *s2; s1++, s2++) ; if (s1 == e1 && s2 == e2) return 0; if (s1 == e1) return 1; if (s2 == e2) return -1; if (*s1 < *s2) return -1; return 1; } char *strstrip(char *buf) { char *end; /* skip initial spaces */ for (; *buf != 0 && isspace(*buf); buf++) ; if (*buf == 0) return buf; end = buf + strlen(buf) - 1; while (end != buf - 1) { if (isspace(*end) == 0) break; *end = 0; end--; } return buf; } char *strlower(char *buf) { char *p = buf; while (*p != 0) *p = tolower(*p); return buf; } void strvacat(char *buf, size_t maxlen, ...) { va_list ap; char *str; size_t len = strlen(buf); va_start(ap, maxlen); while (1) { str = va_arg(ap, char *); if (str == NULL) break; if (len + strlen(str) > maxlen) break; strcat(buf, str); len += strlen(str); } va_end(ap); } int strparsecword(char **inbuf, char *outbuf, size_t maxlen) { char buffer[maxlen]; char *buf = buffer; char *c = *inbuf; char *start; for (; *c != 0 && isspace(*c); c++); if (*c == 0) return 0; if (strlen(*inbuf) > maxlen) return 0; for (; *c != 0; c++) { if (*c == '"') { start = c+1; for (c++; *c != 0 && *c != '"'; c++) { if (*c == '\\') { c++; if (*c == 0) return 0; } } if (*c == 0) return 0; /* dequote the string */ strunescape(start, buf, (int) (c - start + 1), STRESCAPE_QUOTE); buf += strlen(buf); continue; } if (c != *inbuf && isspace(*c) != 0 && isspace(c[-1]) != 0) continue; if (isspace(*c) == 0) return 0; *buf++ = ' '; } *buf = 0; strncpy(outbuf, buffer, maxlen); *inbuf = c; return 1; } int strparsequoteword(char **inbuf, char *outbuf, size_t maxlen) { char *start = *inbuf; char *c; /* skip ws, return if empty */ for (; *start != 0 && isspace(*start); start++); if (*start == 0) return 0; c = start; for (; *c != 0 && isspace(*c) == 0; c++) { if (*c == '"') { for (c++; *c != 0 && *c != '"'; c++) { if (*c == '\\') { c++; if (*c == 0) return 0; } } if (*c == 0) return 0; } if (*c == '[') { for (c++; *c != 0 && *c != ']'; c++); if (*c == 0) return 0; } } /* dequote the string */ strunescape(start, outbuf, (int) (c - start + 1), STRESCAPE_QUOTE); /* skip trailing spaces */ for (; *c != 0 && isspace(*c); c++); *inbuf = c; return 1; } int strgetargc(const char *inbuf) { int count = 1; const char *s; if (inbuf == 0 || *inbuf == 0) return 0; for (s=inbuf; *s != 0; s++) { if (*s == '\\' && *(s+1) == ',') s++; else if (*s == ',') count++; } return count; } int strchoicesplit(const char *inbuf, char **argv, size_t maxnarg) { int argc = 0, i; const char *s = inbuf, *e, *c; char *p; if (inbuf == 0) return 0; INFO(INFO_VERBOSE, "Splitting [%s]", inbuf); while (*s != 0 && argc < maxnarg) { /* skip initial spaces */ while (isspace(*s)) s++; /* find end */ e = s; while (*e != 0) { if (*e == '\\' && (*(e+1) == ',' || *(e+1) == ' ')) e += 2; else if (*e == ',') break; else e++; } argv[argc] = malloc(e-s+1); for (c = s, i = 0; c < e; c++, i++) { if (*c == '\\' && c < (e-1) && (*(c+1) == ',' || *(c+1) == ' ')) { argv[argc][i] = *(c+1); c++; } else argv[argc][i] = *c; } argv[argc][i] = 0; p = &argv[argc][i-1]; /* strip off trailing spaces */ while (p > argv[argc] && *p == ' ') *p-- = 0; argc++; s = e; if (*s == ',') s++; } return argc; } int strchoicesplitsort(const char *origbuf, const char *transbuf, const char *indices, char **oargv, char **targv, int *oindex, size_t maxnarg) { int i; char **cindex; char **sorted_targv; assert(oindex); assert(oargv); assert(targv); assert(origbuf); assert(transbuf); if (strchoicesplit(origbuf, oargv, maxnarg) != maxnarg) return DC_NOTOK; if (strchoicesplit(transbuf, targv, maxnarg) != maxnarg) return DC_NOTOK; if (indices == NULL || *indices == '\0') { for (i = 0; i < maxnarg; i++) oindex[i] = i; } else { cindex = malloc(sizeof(char *) * maxnarg); if (strchoicesplit(indices, cindex, maxnarg) != maxnarg) { INFO(INFO_WARN, "length of indices list '%s' != expected length %zd", indices, maxnarg); /* fall back semi-gracefully to unsorted list */ for (i = 0; i < maxnarg; i++) oindex[i] = i; return maxnarg; } sorted_targv = malloc(sizeof(char *) * maxnarg); for (i = 0; i < maxnarg; i++) { oindex[i] = strtol(cindex[i], NULL, 10) - 1; if (oindex[i] < 0 || oindex[i] >= maxnarg) { int j; INFO(INFO_WARN, "index %d in indices list '%s' out of range", oindex[i] + 1, indices); /* fall back semi-gracefully to unsorted list */ for (j = 0; j < maxnarg; j++) oindex[j] = j; /* Avoid a leak on error */ for (j = 0; j < i; j++) free(sorted_targv[j]); free(sorted_targv); free(cindex); return maxnarg; } sorted_targv[i] = STRDUP(targv[oindex[i]]); } for (i = 0; i < maxnarg; i++) { free(targv[i]); targv[i] = sorted_targv[i]; } free(sorted_targv); free(cindex); } return maxnarg; } int strcmdsplit(char *inbuf, char **argv, size_t maxnarg) { int argc = 0; int inspace = 1; if (maxnarg == 0) return 0; for (; *inbuf != 0; inbuf++) { if (isspace(*inbuf) != 0) { inspace = 1; *inbuf = 0; } else if (inspace != 0) { inspace = 0; argv[argc++] = inbuf; if (argc >= maxnarg) break; } } return argc; } void strunescape(const char *inbuf, char *outbuf, const size_t maxlen, const int mode) { const char *p = inbuf; int i = 0; while (*p != 0 && i < maxlen-1) { if (*p == '\\') { /* Debconf only escapes \n by default */ if (*(p+1) == 'n') { outbuf[i++] = '\n'; p += 2; } /* The configuration also escapes '"' */ else if (mode == STRESCAPE_QUOTE && *(p+1) == '"') { outbuf[i++] = *(p+1); p += 2; } /* The frontend unescapes everything */ else if (mode == STRESCAPE_CAPB) { outbuf[i++] = *(p+1); p += 2; } else outbuf[i++] = *p++; } else outbuf[i++] = *p++; } outbuf[i] = 0; } void strescape(const char *inbuf, char *outbuf, const size_t maxlen, const int mode) { const char *p = inbuf; int i = 0; while (*p != 0 && i < maxlen-1) { /* Debconf only escapes \n by default */ if (*p == '\n') { if (i + 2 >= maxlen) break; outbuf[i++] = '\\'; outbuf[i++] = 'n'; p++; } /* As well as '"' in configuration files */ else if (mode == STRESCAPE_QUOTE && *p == '"') { if (i + 2 >= maxlen) break; outbuf[i++] = '\\'; outbuf[i++] = *p++; } /* And '\' in the escape CAPB */ else if (mode == STRESCAPE_CAPB && *p == '\\') { if (i + 2 >= maxlen) break; outbuf[i++] = '\\'; outbuf[i++] = *p++; } else outbuf[i++] = *p++; } outbuf[i] = 0; } /* These versions allocate their own memory. * TODO: rename to something more self-explanatory */ char *unescapestr(const char *in) { static size_t buflen = 0; static char *buf = NULL; size_t inlen; if (in == 0) return 0; inlen = strlen(in) + 1; /* This is slightly too much memory, because each escaped newline * will be unescaped; but an upper bound is fine. */ if (buflen < inlen) { buflen = inlen; buf = realloc(buf, buflen * sizeof *buf); if (!buf) DIE("Out of memory"); } strunescape(in, buf, buflen, STRESCAPE_822); return buf; } char *escapestr(const char *in) { static size_t buflen = 0; static char *buf = NULL; size_t inlen; const char *p; if (in == 0) return 0; inlen = strlen(in) + 1; /* Each newline will consume an additional byte due to escaping. */ for (p = in; *p; ++p) if (*p == '\n') ++inlen; if (buflen < inlen) { buflen = inlen; buf = realloc(buf, buflen * sizeof *buf); if (!buf) DIE("Out of memory"); } strescape(in, buf, buflen, STRESCAPE_822); return buf; } int strwrap(const char *str, const int width, char *lines[], int maxlines) { #ifdef HAVE_LIBTEXTWRAP textwrap_t p; int j; char *s; char *s0; char *t; textwrap_init(&p); textwrap_columns(&p, width); s0 = s = textwrap(&p, str); for (j=0; j 0) { /* try to fit the most characters */ e = s + width - 1; /* simple case: we got everything */ if (e >= end) { e = end; } else { /* find a breaking point */ while (e > s && !isspace(*e) && *e != '.' && *e != ',') e--; } /* no word-break point found, so just unconditionally break * the line */ if (e == s) e = s + width; /* if there's an explicit linebreak, honor it */ lb = strchr(s, '\n'); if (lb != NULL && lb < e) e = lb; lines[l] = (char *)malloc(e-s+2); strncpy(lines[l], s, e-s+1); lines[l][e-s+1] = 0; CHOMP(lines[l]); len -= (e-s+1); s = e + 1; if (++l >= maxlines) break; } return l; #endif /* HAVE_LIBTEXTWRAP */ } int strlongest(char **strs, int count) { int i, max = 0; size_t width; for (i = 0; i < count; i++) { width = strwidth(strs[i]); if (width > max) max = width; } return max; } size_t strwidth (const char *what) { size_t res; int k; const char *p; wchar_t c; for (res = 0, p = what ; (k = mbtowc (&c, p, MB_LEN_MAX)) > 0 ; p += k) res += wcwidth (c); return res; } /* * Add spaces at the end of string so that strwidth(that) == maxsize * Input string must have been allocated with enough space. */ int strpad (char *what, size_t maxsize) { size_t pos; int k; char *p; wchar_t c; pos = 0; for (p = what; (k = mbtowc (&c, p, MB_LEN_MAX)) > 0; p += k) pos += wcwidth (c); if (pos > maxsize) return 0; for (k = pos; k < maxsize; k++, p++) *p = ' '; *p = '\0'; return 1; } int strtruncate (char *what, size_t maxsize) { size_t pos; int k; char *p; wchar_t c; if (strwidth(what) <= maxsize) return 0; /* Replace end of string with ellipsis "..."; as the last character * may have a double width, stops 4 characters before the end */ pos = 0; for (p = what; (k = mbtowc (&c, p, MB_LEN_MAX)) > 0 && pos < maxsize-5; p += k) pos += wcwidth (c); for (k=0; k < 3; k++, p++) *p = '.'; *p = '\0'; return 1; } int stralign(char **strs, int count) { unsigned int i; unsigned int j; unsigned int * cells_per_line; unsigned int num_columns; unsigned int * column_widths; unsigned int * remaining_line_widths; size_t * column_sizes; size_t * remaining_line_sizes; char * next_cell; char * cell; unsigned int max_width; unsigned int line_width; size_t max_size; size_t line_size; unsigned int column_width; unsigned int cell_width; unsigned int cell_len; unsigned int left_pad; char * new_str; cells_per_line = malloc(sizeof (unsigned int) * count); memset(cells_per_line, 0, sizeof (unsigned int) * count); remaining_line_widths = malloc(sizeof (unsigned int) * count); remaining_line_sizes = malloc(sizeof (size_t) * count); num_columns = 0; column_widths = NULL; column_sizes = NULL; for (i = 0; i < count; i++) { next_cell = strs[i]; for (j = 0; NULL != next_cell; j++) { cells_per_line[i] = j + 1; if (num_columns < cells_per_line[i]) { num_columns = j + 1; column_widths = realloc(column_widths, sizeof (unsigned int) * num_columns); column_widths[j] = 0; column_sizes = realloc(column_sizes, sizeof (size_t) * num_columns); column_sizes[j] = 0; } cell = strsep(&next_cell, STRALIGN_TAB); if (0 == strncmp(STRALIGN_ALIGN_CENTER, cell, 1) || 0 == strncmp(STRALIGN_ALIGN_RIGHT, cell, 1)) { cell++; } if (NULL != next_cell) column_widths[j] = MAX(column_widths[j], strwidth(cell)); else remaining_line_widths[i] = strwidth(cell); } } for (i = 0; i < count; i++) { cell = strs[i]; for (j = 0; j < cells_per_line[i]; j++) { cell_width = strwidth(cell); cell_len = strlen(cell); if (j < cells_per_line[i] - 1) /* one byte padding per character from maximum column width */ column_sizes[j] = MAX(column_sizes[j], cell_len + column_widths[j] - cell_width); else remaining_line_sizes[i] = cell_len; cell = cell + cell_len + 1; } } max_width = 0; for (i = 0; i < count; i++) { line_width = remaining_line_widths[i]; for (j = 0; j < cells_per_line[i] - 1; j++) { line_width += column_widths[j] + 2 /* extra spaces */; } max_width = MAX(max_width, line_width); } max_size = 0; for (i = 0; i < count; i++) { line_size = remaining_line_sizes[i]; for (j = 0; j < cells_per_line[i] - 1; j++) { line_size += column_sizes[j] + 2 /* extra spaces */; } max_size = MAX(max_size, line_size); } free(column_sizes); for (i = 0; i < count; i++) { new_str = malloc(sizeof (char) * max_size + 1); next_cell = new_str; *next_cell = '\0'; cell = strs[i]; for (j = 0; j < cells_per_line[i]; j++) { column_width = j < cells_per_line[i] - 1 ? column_widths[j] : max_width - strwidth(new_str); if (0 == strncmp(STRALIGN_ALIGN_CENTER, cell, 1)) { cell++; cell_width = strwidth(cell); left_pad = (column_width - cell_width) / 2; } else if (0 == strncmp(STRALIGN_ALIGN_RIGHT, cell, 1)) { cell++; cell_width = strwidth(cell); left_pad = column_width - cell_width; } else { left_pad = 0; } strpad(next_cell, left_pad); strcat(next_cell, cell); if (j < cells_per_line[i] - 1) { strpad(next_cell, column_width); next_cell += strlen(next_cell); strcpy(next_cell, " " /* extra space */); next_cell += 2; cell += strlen(cell) + 1; } } free(strs[i]); strs[i] = new_str; } free(column_widths); free(cells_per_line); free(remaining_line_widths); free(remaining_line_sizes); return 0; } #define INITIAL_ROPE_SIZE 128 #define VAR_SIZE 100 struct rope { const char * str; size_t len; }; char * strexpand(const char * src, lookup_function func, void * user_data) { struct rope * rope; void * tmp; size_t rope_size = INITIAL_ROPE_SIZE; unsigned int rope_index = 0; int i; int j; char var[VAR_SIZE]; size_t dest_size = 1; char * dest = NULL;; char * buf; if (NULL == src) { return NULL; } if (NULL == (rope = malloc(sizeof (struct rope) * rope_size))) { return NULL; } /* 1. create the rope */ rope[rope_index].str = src; rope[rope_index].len = 0; for (i = 0; '\0' != src[i]; i++) { if ('$' != src[i] || '{' != src[i + 1]) { rope[rope_index].len++; continue; } if (rope_index >= rope_size - 2 /* room for skips */) { rope_size = rope_size * 2; tmp = realloc(rope, sizeof (struct rope) * rope_size); if (NULL == tmp) { goto end; } rope = tmp; } i += 2; /* skip "${" */ for (j = 0; j < VAR_SIZE && '\0' != src[i] && '}' != src[i]; j++) { var[j] = src[i++]; } if ('\0' == src[i]) { rope[rope_index].len = strlen(rope[rope_index].str); break; } var[j] = '\0'; dest_size += rope[rope_index++].len; rope[rope_index].str = func(var, user_data); /* Handle skips */ if (NULL == rope[rope_index].str) { rope[rope_index].str = &src[i - j - 2 /* "${" */]; rope[rope_index].len = j + 3; /* "${}" */ } else { rope[rope_index].len = strlen(rope[rope_index].str); } dest_size += rope[rope_index++].len; /* skip '}' */ rope[rope_index].str = &src[i + 1]; rope[rope_index].len = 0; } dest_size += rope[rope_index].len; /* 2. dump rope content */ if (NULL == (dest = malloc(dest_size))) { goto end; } buf = dest; for (i = 0; i <= rope_index; i++) { strncpy(buf, rope[i].str, rope[i].len); buf += rope[i].len; } *buf = '\0'; end: free(rope); return dest; } char *strjoinv(const char *sep, va_list ap) { size_t sep_len = strlen(sep); char *str; size_t str_size = 0, str_alloc = 128; const char *arg; str = di_malloc(str_alloc); #define APPEND(piece, len) do { \ if (str_size + (len) + 1 > str_alloc) { \ str_alloc = (str_size + (len) + 1) * 2; \ str = di_realloc(str, str_alloc); \ } \ strncpy(str + str_size, (piece), (len)); \ str_size += (len); \ } while (0) arg = va_arg(ap, const char *); while (arg) { size_t arg_len = strlen(arg); if (str_size) APPEND(sep, sep_len); APPEND(arg, arg_len); arg = va_arg(ap, const char *); } #undef APPEND str[str_size] = '\0'; return str; } char *strjoin(const char *sep, ...) { va_list ap; char *str; va_start(ap, sep); str = strjoinv(sep, ap); va_end(ap); return str; } char *strreplace(const char *src, const char *from, const char *to) { char *str; size_t str_size = 0, str_alloc = 128; char *p; const char *prev; size_t from_len = strlen(from), to_len = strlen(to); str = di_malloc(str_alloc); #define APPEND(piece, len) do { \ if (str_size + (len) + 1 > str_alloc) { \ str_alloc = (str_size + (len) + 1) * 2; \ str = di_realloc(str, str_alloc); \ } \ strncpy(str + str_size, (piece), (len)); \ str_size += (len); \ } while (0) prev = src; for (p = strstr(prev, from); p; p = strstr(prev, from)) { if (p > prev) APPEND(prev, p - prev); APPEND(to, to_len); prev = p + from_len; } if (*prev) APPEND(prev, strlen(prev)); #undef APPEND str[str_size] = '\0'; return str; }