1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Part of Very Secure FTPd
19 * Licence: GPL v2
20 * Author: Chris Evans
21 * str.c
22 *
23 * Generic string handling functions. The fact that a string is implemented
24 * internally using a buffer is not exposed in the API. If you can't see
25 * the buffers, you can't handle them in a screwed way. Or so goes the
26 * theory, anyway...
27 */
28
29#include <string.h>
30
31/* Anti-lamer measures deployed, sir! */
32#define PRIVATE_HANDS_OFF_p_buf p_buf
33#define PRIVATE_HANDS_OFF_len len
34#define PRIVATE_HANDS_OFF_alloc_bytes alloc_bytes
35#include "str.h"
36
37/* Ick. Its for die() */
38#include "utility.h"
39#include "sysutil.h"
40
41/* File local functions */
42static void str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
43                                  const char* p_text, int is_reverse);
44static int str_equal_internal(const char* p_buf1, unsigned int buf1_len,
45                              const char* p_buf2, unsigned int buf2_len);
46
47/* Private functions */
48static void
49s_setbuf(struct mystr* p_str, char* p_newbuf)
50{
51  if (p_str->p_buf != 0)
52  {
53    bug("p_buf not NULL when setting it");
54  }
55  p_str->p_buf = p_newbuf;
56}
57
58void
59private_str_alloc_memchunk(struct mystr* p_str, const char* p_src,
60                           unsigned int len)
61{
62  /* Make sure this will fit in the buffer */
63  unsigned int buf_needed = len + 1;
64  if (buf_needed > p_str->alloc_bytes)
65  {
66    str_free(p_str);
67    s_setbuf(p_str, vsf_sysutil_malloc(buf_needed));
68    p_str->alloc_bytes = buf_needed;
69  }
70  vsf_sysutil_memcpy(p_str->p_buf, p_src, len);
71  p_str->p_buf[len] = '\0';
72  p_str->len = len;
73}
74
75void
76private_str_append_memchunk(struct mystr* p_str, const char* p_src,
77                            unsigned int len)
78{
79  unsigned int buf_needed = p_str->len + len + 1;
80  if (buf_needed > p_str->alloc_bytes)
81  {
82    p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, buf_needed);
83    p_str->alloc_bytes = buf_needed;
84  }
85  vsf_sysutil_memcpy(p_str->p_buf + p_str->len, p_src, len);
86  p_str->p_buf[p_str->len + len] = '\0';
87  p_str->len += len;
88}
89
90/* Public functions */
91void
92str_alloc_text(struct mystr* p_str, const char* p_src)
93{
94  unsigned int len = vsf_sysutil_strlen(p_src);
95  private_str_alloc_memchunk(p_str, p_src, len);
96}
97
98void
99str_copy(struct mystr* p_dest, const struct mystr* p_src)
100{
101  private_str_alloc_memchunk(p_dest, p_src->p_buf, p_src->len);
102}
103
104const char*
105str_strdup(const struct mystr* p_str)
106{
107  return vsf_sysutil_strdup(str_getbuf(p_str));
108}
109
110void
111str_alloc_alt_term(struct mystr* p_str, const char* p_src, char term)
112{
113  const char* p_search = p_src;
114  unsigned int len = 0;
115  while (*p_search != term)
116  {
117    p_search++;
118    len++;
119  }
120  private_str_alloc_memchunk(p_str, p_src, len);
121}
122
123void
124str_alloc_ulong(struct mystr* p_str, unsigned long the_long)
125{
126  str_alloc_text(p_str, vsf_sysutil_ulong_to_str(the_long));
127}
128
129void
130str_alloc_filesize_t(struct mystr* p_str, filesize_t the_filesize)
131{
132  str_alloc_text(p_str, vsf_sysutil_filesize_t_to_str(the_filesize));
133}
134
135void
136str_free(struct mystr* p_str)
137{
138  if (p_str->p_buf != 0)
139  {
140    vsf_sysutil_free(p_str->p_buf);
141  }
142  p_str->p_buf = 0;
143  p_str->len = 0;
144  p_str->alloc_bytes = 0;
145}
146
147void
148str_empty(struct mystr* p_str)
149{
150  /* Ensure a buffer is allocated. */
151  (void) str_getbuf(p_str);
152  str_trunc(p_str, 0);
153}
154
155void
156str_trunc(struct mystr* p_str, unsigned int trunc_len)
157{
158  if (trunc_len >= p_str->alloc_bytes)
159  {
160    bug("trunc_len not smaller than alloc_bytes in str_trunc");
161  }
162  p_str->len = trunc_len;
163  p_str->p_buf[p_str->len] = '\0';
164}
165
166void
167str_reserve(struct mystr* p_str, unsigned int res_len)
168{
169  /* Reserve space for the trailing zero as well. */
170  res_len++;
171  if (res_len > p_str->alloc_bytes)
172  {
173    p_str->p_buf = vsf_sysutil_realloc(p_str->p_buf, res_len);
174    p_str->alloc_bytes = res_len;
175  }
176  p_str->p_buf[res_len - 1] = '\0';
177}
178
179int
180str_isempty(const struct mystr* p_str)
181{
182  return (p_str->len == 0);
183}
184
185unsigned int
186str_getlen(const struct mystr* p_str)
187{
188  return p_str->len;
189}
190
191const char*
192str_getbuf(const struct mystr* p_str)
193{
194  if (p_str->p_buf == 0)
195  {
196    if (p_str->len != 0 || p_str->alloc_bytes != 0)
197    {
198      bug("p_buf NULL and len or alloc_bytes != 0 in str_getbuf");
199    }
200    private_str_alloc_memchunk((struct mystr*)p_str, 0, 0);
201  }
202  return p_str->p_buf;
203}
204
205int
206str_strcmp(const struct mystr* p_str1, const struct mystr* p_str2)
207{
208  return str_equal_internal(p_str1->p_buf, p_str1->len,
209                            p_str2->p_buf, p_str2->len);
210}
211
212static int
213str_equal_internal(const char* p_buf1, unsigned int buf1_len,
214                   const char* p_buf2, unsigned int buf2_len)
215{
216  int retval;
217  unsigned int minlen = buf1_len;
218  if (buf2_len < minlen)
219  {
220    minlen = buf2_len;
221  }
222	//printf("   [str_equal] [%s][%s](%d)(%d)(%d)\n", p_buf1, p_buf2, buf1_len, buf2_len, minlen);	// tmp test
223  retval = vsf_sysutil_memcmp(p_buf1, p_buf2, minlen);
224  if (retval != 0 || buf1_len == buf2_len)
225  {
226    return retval;
227  }
228  /* Strings equal but lengths differ. The greater one, then, is the longer */
229  return (int) (buf1_len - buf2_len);
230}
231
232int
233str_equal(const struct mystr* p_str1, const struct mystr* p_str2)
234{
235  return (str_strcmp(p_str1, p_str2) == 0);
236}
237
238int
239str_equal_text(const struct mystr* p_str, const char* p_text)
240{
241  unsigned int cmplen = vsf_sysutil_strlen(p_text);
242  return (str_equal_internal(p_str->p_buf, p_str->len, p_text, cmplen) == 0);
243}
244
245void
246str_append_str(struct mystr* p_str, const struct mystr* p_other)
247{
248  private_str_append_memchunk(p_str, p_other->p_buf, p_other->len);
249}
250
251void
252str_append_text(struct mystr* p_str, const char* p_src)
253{
254  unsigned int len = vsf_sysutil_strlen(p_src);
255  private_str_append_memchunk(p_str, p_src, len);
256}
257
258void
259str_append_char(struct mystr* p_str, char the_char)
260{
261  private_str_append_memchunk(p_str, &the_char, sizeof(the_char));
262}
263
264void
265str_append_ulong(struct mystr* p_str, unsigned long the_ulong)
266{
267  str_append_text(p_str, vsf_sysutil_ulong_to_str(the_ulong));
268}
269
270void
271str_append_filesize_t(struct mystr* p_str, filesize_t the_filesize)
272{
273  str_append_text(p_str, vsf_sysutil_filesize_t_to_str(the_filesize));
274}
275
276void
277str_append_double(struct mystr* p_str, double the_double)
278{
279  str_append_text(p_str, vsf_sysutil_double_to_str(the_double));
280}
281
282void
283str_upper(struct mystr* p_str)
284{
285  unsigned int i;
286  for (i=0; i < p_str->len; i++)
287  {
288    p_str->p_buf[i] = vsf_sysutil_toupper(p_str->p_buf[i]);
289  }
290}
291
292void
293str_rpad(struct mystr* p_str, const unsigned int min_width)
294{
295  unsigned int to_pad;
296  if (p_str->len >= min_width)
297  {
298    return;
299  }
300  to_pad = min_width - p_str->len;
301  while (to_pad--)
302  {
303    str_append_char(p_str, ' ');
304  }
305}
306
307void
308str_lpad(struct mystr* p_str, const unsigned int min_width)
309{
310  static struct mystr s_tmp_str;
311  unsigned int to_pad;
312  if (p_str->len >= min_width)
313  {
314    return;
315  }
316  to_pad = min_width - p_str->len;
317  str_empty(&s_tmp_str);
318  while (to_pad--)
319  {
320    str_append_char(&s_tmp_str, ' ');
321  }
322  str_append_str(&s_tmp_str, p_str);
323  str_copy(p_str, &s_tmp_str);
324}
325
326void
327str_replace_char(struct mystr* p_str, char from, char to)
328{
329  unsigned int i;
330  for (i=0; i < p_str->len; i++)
331  {
332    if (p_str->p_buf[i] == from)
333    {
334      p_str->p_buf[i] = to;
335    }
336  }
337}
338
339void
340str_replace_text(struct mystr* p_str, const char* p_from, const char* p_to)
341{
342  static struct mystr s_lhs_chunk_str;
343  static struct mystr s_rhs_chunk_str;
344  unsigned int lhs_len;
345  str_copy(&s_lhs_chunk_str, p_str);
346  str_free(p_str);
347  do
348  {
349    lhs_len = str_getlen(&s_lhs_chunk_str);
350    str_split_text(&s_lhs_chunk_str, &s_rhs_chunk_str, p_from);
351    /* Copy lhs to destination */
352    str_append_str(p_str, &s_lhs_chunk_str);
353    /* If this was a 'hit', append the 'to' text */
354    if (str_getlen(&s_lhs_chunk_str) < lhs_len)
355    {
356      str_append_text(p_str, p_to);
357    }
358    /* Current rhs becomes new lhs */
359    str_copy(&s_lhs_chunk_str, &s_rhs_chunk_str);
360  } while (!str_isempty(&s_lhs_chunk_str));
361}
362
363void
364str_split_char(struct mystr* p_src, struct mystr* p_rhs, char c)
365{
366  /* Just use str_split_text */
367  char ministr[2];
368  ministr[0] = c;
369  ministr[1] = '\0';
370  str_split_text(p_src, p_rhs, ministr);
371}
372
373void
374str_split_char_reverse(struct mystr* p_src, struct mystr* p_rhs, char c)
375{
376  /* Just use str_split_text_reverse */
377  char ministr[2];
378  ministr[0] = c;
379  ministr[1] = '\0';
380  str_split_text_reverse(p_src, p_rhs, ministr);
381}
382
383void
384str_split_text(struct mystr* p_src, struct mystr* p_rhs, const char* p_text)
385{
386  str_split_text_common(p_src, p_rhs, p_text, 0);
387}
388
389void
390str_split_text_reverse(struct mystr* p_src, struct mystr* p_rhs,
391                       const char* p_text)
392{
393  str_split_text_common(p_src, p_rhs, p_text, 1);
394}
395
396static void
397str_split_text_common(struct mystr* p_src, struct mystr* p_rhs,
398                      const char* p_text, int is_reverse)
399{
400  struct str_locate_result locate_result;
401  unsigned int indexx;
402  unsigned int search_len = vsf_sysutil_strlen(p_text);
403  if (is_reverse)
404  {
405    locate_result = str_locate_text_reverse(p_src, p_text);
406  }
407  else
408  {
409    locate_result = str_locate_text(p_src, p_text);
410  }
411  /* Not found? */
412  if (!locate_result.found)
413  {
414    str_empty(p_rhs);
415    return;
416  }
417  indexx = locate_result.index;
418  if (indexx + search_len > p_src->len)
419  {
420    bug("indexx invalid in str_split_text");
421  }
422  /* Build rhs */
423  private_str_alloc_memchunk(p_rhs, p_src->p_buf + indexx + search_len,
424                             p_src->len - indexx - search_len);
425  /* Build lhs */
426  str_trunc(p_src, indexx);
427}
428
429struct str_locate_result
430str_locate_str(const struct mystr* p_str, const struct mystr* p_look_str)
431{
432  return str_locate_text(p_str, str_getbuf(p_look_str));
433}
434
435struct str_locate_result
436str_locate_str_reverse(const struct mystr* p_str,
437                       const struct mystr* p_look_str)
438{
439  return str_locate_text_reverse(p_str, str_getbuf(p_look_str));
440}
441
442struct str_locate_result
443str_locate_char(const struct mystr* p_str, char look_char)
444{
445  char look_str[2];
446  look_str[0] = look_char;
447  look_str[1] = '\0';
448  return str_locate_text(p_str, look_str);
449}
450
451struct str_locate_result
452str_locate_chars(const struct mystr* p_str, const char* p_chars)
453{
454  struct str_locate_result retval;
455  unsigned int num_chars = vsf_sysutil_strlen(p_chars);
456  unsigned int i = 0;
457
458  memset(&retval, 0, sizeof(struct str_locate_result));
459  retval.found = 0;
460  for (; i < p_str->len; ++i)
461  {
462    unsigned int j = 0;
463    char this_char = p_str->p_buf[i];
464    for (; j < num_chars; ++j)
465    {
466      if (p_chars[j] == this_char)
467      {
468        retval.found = 1;
469        retval.index = i;
470        retval.char_found = p_chars[j];
471        return retval;
472      }
473    }
474  }
475  return retval;
476}
477
478struct str_locate_result
479str_locate_text(const struct mystr* p_str, const char* p_text)
480{
481  struct str_locate_result retval;
482  unsigned int i;
483  unsigned int text_len = vsf_sysutil_strlen(p_text);
484  retval.found = 0;
485  retval.index = 0;
486  if (text_len == 0 || text_len > p_str->len)
487  {
488    /* Not found */
489    return retval;
490  }
491  for (i=0; i <= (p_str->len - text_len); i++)
492  {
493    if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
494    {
495      retval.found = 1;
496      retval.index = i;
497      return retval;
498    }
499  }
500  /* Not found */
501  return retval;
502}
503
504struct str_locate_result
505str_locate_text_reverse(const struct mystr* p_str, const char* p_text)
506{
507  struct str_locate_result retval;
508  unsigned int i;
509  unsigned int text_len = vsf_sysutil_strlen(p_text);
510  retval.found = 0;
511  retval.index = 0;
512  if (text_len == 0 || text_len > p_str->len)
513  {
514    return retval;
515  }
516  i = p_str->len - text_len;
517  /* Want to go through loop once even if i==0 */
518  while (1)
519  {
520    if (vsf_sysutil_memcmp(p_str->p_buf + i, p_text, text_len) == 0)
521    {
522      retval.found = 1;
523      retval.index = i;
524      return retval;
525    }
526    if (i == 0)
527    {
528      break;
529    }
530    i--;
531  }
532  /* Not found */
533  return retval;
534}
535
536void
537str_left(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
538{
539  if (chars > p_str->len)
540  {
541    bug("chars invalid in str_left");
542  }
543  private_str_alloc_memchunk(p_out, p_str->p_buf, chars);
544}
545
546void
547str_right(const struct mystr* p_str, struct mystr* p_out, unsigned int chars)
548{
549  unsigned int indexx = p_str->len - chars;
550  if (chars > p_str->len)
551  {
552    bug("chars invalid in str_right");
553  }
554  private_str_alloc_memchunk(p_out, p_str->p_buf + indexx, chars);
555}
556
557void
558str_mid_to_end(const struct mystr* p_str, struct mystr* p_out,
559               unsigned int indexx)
560{
561  if (indexx > p_str->len)
562  {
563    bug("invalid indexx in str_mid_to_end");
564  }
565  private_str_alloc_memchunk(p_out, p_str->p_buf + indexx,
566                             p_str->len - indexx);
567}
568
569char
570str_get_char_at(const struct mystr* p_str, const unsigned int indexx)
571{
572  if (indexx >= p_str->len)
573  {
574    bug("bad indexx in str_get_char_at");
575  }
576  return p_str->p_buf[indexx];
577}
578
579int
580str_contains_space(const struct mystr* p_str)
581{
582  unsigned int i;
583  for (i=0; i < p_str->len; i++)
584  {
585    if (vsf_sysutil_isspace(p_str->p_buf[i]))
586    {
587      return 1;
588    }
589  }
590  return 0;
591}
592
593int
594str_contains_unprintable(const struct mystr* p_str)
595{
596  unsigned int i;
597  for (i=0; i < p_str->len; i++)
598  {
599    if (!vsf_sysutil_isprint(p_str->p_buf[i]))
600    {
601      return 1;
602    }
603  }
604  return 0;
605}
606
607int
608str_atoi(const struct mystr* p_str)
609{
610  return vsf_sysutil_atoi(str_getbuf(p_str));
611}
612
613filesize_t
614str_a_to_filesize_t(const struct mystr* p_str)
615{
616  return vsf_sysutil_a_to_filesize_t(str_getbuf(p_str));
617}
618
619unsigned int
620str_octal_to_uint(const struct mystr* p_str)
621{
622  return vsf_sysutil_octal_to_uint(str_getbuf(p_str));
623}
624
625int
626str_getline(const struct mystr* p_str, struct mystr* p_line_str,
627            unsigned int* p_pos)
628{
629  unsigned int start_pos = *p_pos;
630  unsigned int curr_pos = start_pos;
631  unsigned int buf_len = str_getlen(p_str);
632  const char* p_buf = str_getbuf(p_str);
633  unsigned int out_len;
634  if (start_pos > buf_len)
635  {
636    bug("p_pos out of range in str_getline");
637  }
638  str_empty(p_line_str);
639  if (start_pos == buf_len)
640  {
641    return 0;
642  }
643  while (curr_pos < buf_len && p_buf[curr_pos] != '\n')
644  {
645    curr_pos++;
646  }
647  out_len = curr_pos - start_pos;
648  /* If we ended on a \n - skip it */
649  if (curr_pos < buf_len && p_buf[curr_pos] == '\n')
650  {
651    curr_pos++;
652  }
653  private_str_alloc_memchunk(p_line_str, p_buf + start_pos, out_len);
654  *p_pos = curr_pos;
655  return 1;
656}
657
658int
659str_contains_line(const struct mystr* p_str, const struct mystr* p_line_str)
660{
661  static struct mystr s_curr_line_str;
662  unsigned int pos = 0;
663  while (str_getline(p_str, &s_curr_line_str, &pos))
664  {
665    if (str_equal(&s_curr_line_str, p_line_str))
666    {
667      return 1;
668    }
669  }
670  return 0;
671}
672
673void
674str_replace_unprintable(struct mystr* p_str, char new_char)
675{
676  unsigned int i;
677  for (i=0; i < p_str->len; i++)
678  {
679    if (!vsf_sysutil_isprint(p_str->p_buf[i]))
680    {
681      p_str->p_buf[i] = new_char;
682    }
683  }
684}
685
686