コード例 #1
0
ファイル: iptv_http.c プロジェクト: flyingfridge/tvheadend
/*
 * Receive data
 */
static int
iptv_http_data
  ( http_client_t *hc, void *buf, size_t len )
{
  iptv_mux_t *im = hc->hc_aux;

  if (im == NULL || hc->hc_code != HTTP_STATUS_OK)
    return 0;

  if (im->im_m3u_header) {
    sbuf_append(&im->mm_iptv_buffer, buf, len);
    return 0;
  }

  pthread_mutex_lock(&iptv_lock);

  sbuf_append(&im->mm_iptv_buffer, buf, len);
  tsdebug_write((mpegts_mux_t *)im, buf, len);

  if (len > 0)
    iptv_input_recv_packets(im, len);

  pthread_mutex_unlock(&iptv_lock);

  return 0;
}
コード例 #2
0
ファイル: tr.c プロジェクト: litcave/neatroff
/* read arguments for .ds and .char */
static void mkargs_ds(struct sbuf *sbuf)
{
	char *s = read_name(n_cp);
	sbuf_append(sbuf, s);
	sbuf_add(sbuf, 0);
	free(s);
	s = read_string();
	if (s) {
		sbuf_append(sbuf, s);
		sbuf_add(sbuf, 0);
		free(s);
	}
	jmp_eol();
}
コード例 #3
0
ファイル: tr.c プロジェクト: litcave/neatroff
static void tr_coa(char **args)
{
	char *src = args[1];
	char *dst = args[2];
	if (src && dst && str_get(map(src))) {
		struct sbuf sb;
		sbuf_init(&sb);
		if (str_get(map(dst)))
			sbuf_append(&sb, str_get(map(dst)));
		sbuf_append(&sb, str_get(map(src)));
		str_set(map(dst), sbuf_buf(&sb));
		sbuf_done(&sb);
	}
}
コード例 #4
0
ファイル: avc.c プロジェクト: wmyrda/tvheadend
static int
avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size)
{
  const uint8_t *p = buf_in;
  const uint8_t *end = p + size;
  const uint8_t *nal_start, *nal_end;

  //printf("CONVERT SIZE %d\n", size);

  size = 0;
  nal_start = avc_find_startcode(p, end);
  while (nal_start < end) {
    while(!*(nal_start++));
    nal_end = avc_find_startcode(nal_start, end);
    /*printf("%4d bytes  %5d : %d\n", nal_end - nal_start,
      nal_start - buf_in,
      nal_end   - buf_in);*/

    int l = nal_end - nal_start;

    if (l) {
      sbuf_put_be32(sb, l);
      sbuf_append(sb, nal_start, l);
      size += 4 + l;
    }
    nal_start = nal_end;
  }
  return size;
}
コード例 #5
0
ファイル: iptv_udp.c プロジェクト: ProfYaffle/tvheadend
static ssize_t
iptv_udp_read ( iptv_input_t *mi, iptv_mux_t *im )
{
  int i, n;
  struct iovec *iovec;
  udp_multirecv_t *um = im->im_data;
  ssize_t res = 0;

  n = udp_multirecv_read(um, im->mm_iptv_fd, IPTV_PKTS, &iovec);
  if (n < 0)
    return -1;

  im->mm_iptv_rtp_seq &= ~0xfff;
  for (i = 0; i < n; i++, iovec++) {
    if (iovec->iov_len <= 0)
      continue;
    if (*(uint8_t *)iovec->iov_base != 0x47) {
      im->mm_iptv_rtp_seq++;
      continue;
    }
    sbuf_append(&im->mm_iptv_buffer, iovec->iov_base, iovec->iov_len);
    res += iovec->iov_len;
  }

  if (im->mm_iptv_rtp_seq < 0xffff && im->mm_iptv_rtp_seq > 0x3ff) {
    tvherror(LS_IPTV, "receving non-raw UDP data for %s!", im->mm_nicename);
    im->mm_iptv_rtp_seq = 0x10000; /* no further logs! */
  }

  return res;
}
コード例 #6
0
ファイル: tr.c プロジェクト: litcave/neatroff
static void macrobody(struct sbuf *sbuf, char *end)
{
	int first = 1;
	int c;
	char *req = NULL;
	cp_back('\n');
	cp_copymode(1);
	while ((c = cp_next()) >= 0) {
		if (sbuf && !first)
			sbuf_add(sbuf, c);
		first = 0;
		if (c == '\n') {
			if ((c = cp_next()) != c_cc) {
				cp_back(c);
				continue;
			}
			req = read_name(n_cp);
			if (!strcmp(end, req)) {
				in_push(end, NULL);
				cp_back(c_cc);
				break;
			}
			if (sbuf) {
				sbuf_add(sbuf, c_cc);
				sbuf_append(sbuf, req);
			}
			free(req);
			req = NULL;
		}
	}
	free(req);
	cp_copymode(0);
}
コード例 #7
0
ファイル: iptv_http.c プロジェクト: astrilchuk/tvheadend
/*
 * Complete data
 */
static int
iptv_http_complete
  ( http_client_t *hc )
{
  iptv_mux_t *im = hc->hc_aux;
  char *url;
  url_t u;
  int r;

  if (im->im_m3u_header) {
    im->im_m3u_header = 0;
    sbuf_append(&im->mm_iptv_buffer, "", 1);
    url = iptv_http_m3u((char *)im->mm_iptv_buffer.sb_data);
    sbuf_reset(&im->mm_iptv_buffer, IPTV_BUF_SIZE);
    if (url == NULL) {
      tvherror("iptv", "m3u contents parsing failed");
      return 0;
    }
    urlinit(&u);
    if (!urlparse(url, &u)) {
      hc->hc_keepalive = 0;
      r = http_client_simple_reconnect(hc, &u);
      if (r < 0)
        tvherror("iptv", "cannot reopen http client: %d'", r);
    } else {
      tvherror("iptv", "m3u url invalid '%s'", url);
    }
    urlreset(&u);
    free(url);
    return 0;
  }
  return 0;
}
コード例 #8
0
ファイル: tr.c プロジェクト: litcave/neatroff
/* read arguments for .nr */
static void mkargs_reg1(struct sbuf *sbuf)
{
	char *s = read_name(n_cp);
	sbuf_append(sbuf, s);
	sbuf_add(sbuf, 0);
	free(s);
	mkargs_req(sbuf);
}
コード例 #9
0
ファイル: tr.c プロジェクト: litcave/neatroff
/* read arguments for .ochar */
static void mkargs_ochar(struct sbuf *sbuf)
{
	char *s = read_name(0);
	sbuf_append(sbuf, s);
	sbuf_add(sbuf, 0);
	free(s);
	mkargs_ds(sbuf);
}
コード例 #10
0
ファイル: iptv_http.c プロジェクト: flyingfridge/tvheadend
/*
 * Complete data
 */
static int
iptv_http_complete
  ( http_client_t *hc )
{
  iptv_mux_t *im = hc->hc_aux;
  char *url, *url2, *s, *p;
  url_t u;
  int r;

  if (im->im_m3u_header) {
    im->im_m3u_header = 0;
    sbuf_append(&im->mm_iptv_buffer, "", 1);
    url = iptv_http_m3u((char *)im->mm_iptv_buffer.sb_data);
    sbuf_reset(&im->mm_iptv_buffer, IPTV_BUF_SIZE);
    if (url == NULL) {
      tvherror("iptv", "m3u contents parsing failed");
      return 0;
    }
    urlinit(&u);
    if (url[0] == '/') {
      s = strdupa(im->mm_iptv_url_raw);
      if ((p = strchr(s, '/')) != NULL)
        *p = '\0';
      if (!urlparse(s, &u))
        goto invalid;
      url2 = malloc(512);
      url2[0] = '\0';
      if ((p = http_arg_get(&hc->hc_args, "Host")) != NULL) {
        snprintf(url2, 512, "%s://%s%s",
                 hc->hc_ssl ? "https" : "http", p, url);
      } else if (im->mm_iptv_url_raw) {
        snprintf(url2, 512, "%s%s", s, url);
      }
      free(url);
      url = url2;
      urlinit(&u);
    }
    if (!urlparse(url, &u)) {
      hc->hc_keepalive = 0;
      r = http_client_simple_reconnect(hc, &u, HTTP_VERSION_1_1);
      if (r < 0)
        tvherror("iptv", "cannot reopen http client: %d'", r);
    } else {
invalid:
      tvherror("iptv", "m3u url invalid '%s'", url);
    }
    urlreset(&u);
    free(url);
    return 0;
  }
  return 0;
}
コード例 #11
0
ファイル: iptv_file.c プロジェクト: Leyorus/tvheadend
/*
 * Read thread
 */
static void *
iptv_file_thread ( void *aux )
{
  iptv_mux_t *im = aux;
  file_priv_t *fp = im->im_data;
  ssize_t r;
  int fd = fp->fd, pause = 0;
  char buf[32*1024];
  off_t off = 0;
  int64_t mono;
  int e;

#if defined(PLATFORM_DARWIN)
  fcntl(fd, F_NOCACHE, 1);
#endif
  pthread_mutex_lock(&iptv_lock);
  while (!fp->shutdown && fd > 0) {
    while (!fp->shutdown && pause) {
      mono = mclk() + sec2mono(1);
      do {
        e = tvh_cond_timedwait(&fp->cond, &iptv_lock, mono);
        if (e == ETIMEDOUT)
          break;
      } while (ERRNO_AGAIN(e));
    }
    if (fp->shutdown)
      break;
    pause = 0;
    pthread_mutex_unlock(&iptv_lock);
    r = read(fd, buf, sizeof(buf));
    pthread_mutex_lock(&iptv_lock);
    if (r == 0)
      break;
    if (r < 0) {
      if (ERRNO_AGAIN(errno))
        continue;
      break;
    }
    sbuf_append(&im->mm_iptv_buffer, buf, r);
    if (iptv_input_recv_packets(im, r) == 1)
      pause = 1;
#ifndef PLATFORM_DARWIN
#if !ENABLE_ANDROID
    posix_fadvise(fd, off, r, POSIX_FADV_DONTNEED);
#endif
#endif
    off += r;
  }
  pthread_mutex_unlock(&iptv_lock);
  return NULL;
}
コード例 #12
0
ファイル: iptv_rtcp.c プロジェクト: Glenn-1990/tvheadend
/*
 Append RTCP header data to the buffer.
 Version and padding are set to fixed values, i.e. 2 and 0;
 */
static void
rtcp_append_headers(sbuf_t *buffer, rtcp_t *packet)
{
  packet->common.version = 2;
  packet->common.p = 0;
  
  uint8_t byte = 0;
  byte |= packet->common.version << 6;
  byte |= packet->common.p << 5;
  byte |= packet->common.count;
  sbuf_put_byte(buffer, byte);
  byte = packet->common.pt;
  sbuf_put_byte(buffer, byte);
  sbuf_append(buffer, &packet->common.length, sizeof(packet->common.length));
}
コード例 #13
0
ファイル: tr.c プロジェクト: litcave/neatroff
static void tr_chop(char **args)
{
	struct sbuf sbuf;
	int id;
	id = map(args[1]);
	if (str_get(id)) {
		sbuf_init(&sbuf);
		sbuf_append(&sbuf, str_get(id));
		if (!sbuf_empty(&sbuf)) {
			sbuf_cut(&sbuf, sbuf_len(&sbuf) - 1);
			str_set(id, sbuf_buf(&sbuf));
		}
		sbuf_done(&sbuf);
	}
}
コード例 #14
0
ファイル: tr.c プロジェクト: litcave/neatroff
static void tr_de(char **args)
{
	struct sbuf sbuf;
	int id;
	if (!args[1])
		return;
	id = map(args[1]);
	sbuf_init(&sbuf);
	if (args[0][1] == 'a' && args[0][2] == 'm' && str_get(id))
		sbuf_append(&sbuf, str_get(id));
	macrobody(&sbuf, args[2] ? args[2] : ".");
	str_set(id, sbuf_buf(&sbuf));
	sbuf_done(&sbuf);
	if (!n_cp && args[3])	/* parse the arguments as request argv[3] */
		str_dset(id, str_dget(map(args[3])));
}
コード例 #15
0
ファイル: tr.c プロジェクト: litcave/neatroff
static void tr_coi(char **args)
{
	char *reg = args[1];
	char *path = args[2];
	char buf[1024];
	FILE *fp;
	if (!reg || !reg[0] || !path || !path[0])
		return;
	if ((fp = fopen(path + 1, "r"))) {
		struct sbuf sb;
		sbuf_init(&sb);
		while (fgets(buf, sizeof(buf), fp))
			sbuf_append(&sb, buf);
		str_set(map(reg), sbuf_buf(&sb));
		sbuf_done(&sb);
		fclose(fp);
	}
}
コード例 #16
0
ファイル: sbuf.c プロジェクト: beh9540/bacnetwx2
void testStaticBuffer(
    Test * pTest)
{
    STATIC_BUFFER sbuffer;
    char *data1 = "Joshua";
    char *data2 = "Anna";
    char *data3 = "Christopher";
    char *data4 = "Mary";
    char data_buffer[480] = "";
    char test_data_buffer[480] = "";
    char *data;
    unsigned count;

    sbuf_init(&sbuffer, NULL, 0);
    ct_test(pTest, sbuf_empty(&sbuffer) == true);
    ct_test(pTest, sbuf_data(&sbuffer) == NULL);
    ct_test(pTest, sbuf_size(&sbuffer) == 0);
    ct_test(pTest, sbuf_count(&sbuffer) == 0);
    ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == false);

    sbuf_init(&sbuffer, data_buffer, sizeof(data_buffer));
    ct_test(pTest, sbuf_empty(&sbuffer) == true);
    ct_test(pTest, sbuf_data(&sbuffer) == data_buffer);
    ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
    ct_test(pTest, sbuf_count(&sbuffer) == 0);

    ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == true);
    ct_test(pTest, sbuf_append(&sbuffer, data2, strlen(data2)) == true);
    ct_test(pTest, sbuf_append(&sbuffer, data3, strlen(data3)) == true);
    ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
    strcat(test_data_buffer, data1);
    strcat(test_data_buffer, data2);
    strcat(test_data_buffer, data3);
    strcat(test_data_buffer, data4);
    ct_test(pTest, sbuf_count(&sbuffer) == strlen(test_data_buffer));

    data = sbuf_data(&sbuffer);
    count = sbuf_count(&sbuffer);
    ct_test(pTest, memcmp(data, test_data_buffer, count) == 0);
    ct_test(pTest, count == strlen(test_data_buffer));

    ct_test(pTest, sbuf_truncate(&sbuffer, 0) == true);
    ct_test(pTest, sbuf_count(&sbuffer) == 0);
    ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer));
    ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true);
    data = sbuf_data(&sbuffer);
    count = sbuf_count(&sbuffer);
    ct_test(pTest, memcmp(data, data4, count) == 0);
    ct_test(pTest, count == strlen(data4));

    return;
}
コード例 #17
0
ファイル: download.c プロジェクト: Ferni7/tvheadend
static void
download_pipe_read(void *aux)
{
  download_t *dn = aux;
  ssize_t len;
  char *s, *p;

  if (dn->pipe_fd < 0 || dn->pipe_pid == 0)
    return;

  while (1) {
    if (dn->pipe_sbuf.sb_ptr > 50*1024*1024) {
      errno = EMSGSIZE;
      goto failed;
    }
    sbuf_alloc(&dn->pipe_sbuf, 2048);
    len = sbuf_read(&dn->pipe_sbuf, dn->pipe_fd);
    if (len == 0) {
      s = dn->url ? strdupa(dn->url) : strdupa("");
      p = strchr(s, ' ');
      if (p)
        *p = '\0';
      p = strrchr(s, '/');
      if (p)
        p++;
      sbuf_append(&dn->pipe_sbuf, "", 1);
      dn->process(dn->aux, p, NULL, (char *)dn->pipe_sbuf.sb_data, (size_t)dn->pipe_sbuf.sb_ptr);
      download_pipe_close(dn);
      return;
    } else if (len < 0) {
      if (ERRNO_AGAIN(errno))
        break;
failed:
      tvherror(dn->log, "pipe: read failed: %d", errno);
      download_pipe_close(dn);
      return;
    }
  }

  gtimer_arm_ms(&dn->pipe_read_timer, download_pipe_read, dn, 250);
}
コード例 #18
0
ファイル: tr.c プロジェクト: litcave/neatroff
/* read into sbuf until stop; if stop is NULL, stop at whitespace */
static int read_until(struct sbuf *sbuf, char *stop,
			int (*next)(void), void (*back)(int))
{
	char cs[GNLEN], cs2[GNLEN];
	int c;
	while ((c = next()) >= 0) {
		if (c == c_ni)
			continue;
		back(c);
		if (c == '\n')
			return 1;
		if (!stop && (c == ' ' || c == '\t'))
			return 0;
		charnext(cs, next, back);
		if (stop && !strcmp(stop, cs))
			return 0;
		charnext_str(cs2, cs);
		sbuf_append(sbuf, cs2);
	}
	return 1;
}
コード例 #19
0
ファイル: parser_avc.c プロジェクト: BATYD-Turksat/tvheadend
static int avc_parse_nal_units(sbuf_t *sb, const uint8_t *buf_in, int size)
{
  const uint8_t *p = buf_in;
  const uint8_t *end = p + size;
  const uint8_t *nal_start, *nal_end;

  size = 0;
  nal_start = avc_find_startcode(p, end);
  for (;;) {
    while (nal_start < end && !*(nal_start++));
    if (nal_start == end)
      break;
    
    nal_end = avc_find_startcode(nal_start, end);

    int l = nal_end - nal_start;

    sbuf_put_be32(sb, l);
    sbuf_append(sb, nal_start, l);
    size += 4 + l;
    nal_start = nal_end;
  }
  return size;
}
コード例 #20
0
ファイル: iptv_rtcp.c プロジェクト: Glenn-1990/tvheadend
/*
 Append RTCP receiver report data to the buffer.
 */
static void
rtcp_append_rr(sbuf_t *buffer, rtcp_t *packet)
{
  uint8_t byte = 0;
  rtcp_rr_t report = packet->r.rr.rr[0];
  
  sbuf_append(buffer, &packet->r.rr.ssrc, sizeof(packet->r.rr.ssrc));
  sbuf_append(buffer, &report.ssrc, sizeof(report.ssrc));
  byte = report.fraction;
  sbuf_put_byte(buffer, byte);
  
  // Set the lost number in 3 times
  byte = (report.lost & 0xff0000) >> 16;
  sbuf_put_byte(buffer, byte);
  byte = (report.lost & 0x00ff00) >> 8;
  sbuf_put_byte(buffer, byte);
  byte = report.lost & 0x0000ff;
  sbuf_put_byte(buffer, byte);
  
  sbuf_append(buffer, &report.last_seq, sizeof(report.last_seq));
  sbuf_append(buffer, &report.jitter, sizeof(report.jitter));
  sbuf_append(buffer, &report.lsr, sizeof(report.lsr));
  sbuf_append(buffer, &report.dlsr, sizeof(report.dlsr));
}
コード例 #21
0
ファイル: avc.c プロジェクト: wmyrda/tvheadend
static int
isom_write_avcc(sbuf_t *sb, const uint8_t *data, int len)
{
  if (len > 6) {
    /* check for h264 start code */
    if (RB32(data) == 0x00000001 ||
	RB24(data) == 0x000001) {
      uint8_t *buf=NULL, *end, *start;
      uint32_t *sps_size_array=0, *pps_size_array=0;
      uint32_t pps_count=0,sps_count=0;
      uint8_t **sps_array=0, **pps_array=0;
      int i;

      int ret = avc_parse_nal_units_buf(data, &buf, &len);
      if (ret < 0)
	return ret;
      start = buf;
      end = buf + len;

      /* look for sps and pps */
      while (buf < end) {
	unsigned int size;
	uint8_t nal_type;
	size = RB32(buf);
	nal_type = buf[4] & 0x1f;
	if (nal_type == 7) { /* SPS */
	  sps_array = realloc(sps_array,sizeof(uint8_t*)*(sps_count+1));
	  sps_size_array = realloc(sps_size_array,sizeof(uint32_t)*(sps_count+1));
	  sps_array[sps_count] = buf + 4;
	  sps_size_array[sps_count] = size;
	  sps_count++;
	} else if (nal_type == 8) { /* PPS */
	  pps_size_array = realloc(pps_size_array,sizeof(uint32_t)*(pps_count+1));
	  pps_array = realloc(pps_array,sizeof (uint8_t*)*(pps_count+1));
	  pps_array[pps_count] = buf + 4;
	  pps_size_array[pps_count] = size;
	  pps_count++;
	}
	buf += size + 4;
      }
      if(!sps_count || !pps_count) {
	free(start);
	if (sps_count)
	  free(sps_array);
	if (pps_count)
	  free(pps_array);
	return -1;
      }

      sbuf_put_byte(sb, 1); /* version */
      sbuf_put_byte(sb, sps_array[0][1]); /* profile */
      sbuf_put_byte(sb, sps_array[0][2]); /* profile compat */
      sbuf_put_byte(sb, sps_array[0][3]); /* level */
      sbuf_put_byte(sb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
      sbuf_put_byte(sb, 0xe0+sps_count); /* 3 bits reserved (111) + 5 bits number of sps (00001) */
      for (i=0;i<sps_count;i++) {
	sbuf_put_be16(sb, sps_size_array[i]);
	sbuf_append(sb, sps_array[i], sps_size_array[i]);
      }

      sbuf_put_byte(sb, pps_count); /* number of pps */
      for (i=0;i<pps_count;i++) {
	sbuf_put_be16(sb, pps_size_array[i]);
	sbuf_append(sb, pps_array[i], pps_size_array[i]);
      }
      free(start);

      if (sps_count)
	free(sps_array);
      if (pps_count)
	free(pps_array);
    } else {
      sbuf_append(sb, data, len);
    }
  }
  return 0;
}
コード例 #22
0
ファイル: iptv_udp.c プロジェクト: ProfYaffle/tvheadend
ssize_t
iptv_rtp_read ( iptv_mux_t *im, udp_multirecv_t *um,
                void (*pkt_cb)(iptv_mux_t *im, uint8_t *pkt, int len) )
{
  ssize_t len, hlen;
  uint8_t *rtp;
  int i, n;
  uint32_t seq, nseq, unc = 0;
  struct iovec *iovec;
  ssize_t res = 0;

  n = udp_multirecv_read(um, im->mm_iptv_fd, IPTV_PKTS, &iovec);
  if (n < 0)
    return -1;

  seq = im->mm_iptv_rtp_seq;

  for (i = 0; i < n; i++, iovec++) {

    /* Raw packet */
    rtp = iovec->iov_base;
    len = iovec->iov_len;

    /* Strip RTP header */
    if (len < 12)
      continue;

    if (pkt_cb)
      pkt_cb(im, rtp, len);

    /* Version 2 */
    if ((rtp[0] & 0xC0) != 0x80)
      continue;

    /* MPEG-TS */
    if ((rtp[1] & 0x7F) != 33)
      continue;

    /* Header length (4bytes per CSRC) */
    hlen = ((rtp[0] & 0xf) * 4) + 12;
    if (rtp[0] & 0x10) {
      if (len < hlen+4)
        continue;
      hlen += ((rtp[hlen+2] << 8) | rtp[hlen+3]) * 4;
      hlen += 4;
    }
    if (len < hlen || ((len - hlen) % 188) != 0)
      continue;

    len -= hlen;

    /* Use uncorrectable value to notify RTP delivery issues */
    nseq = (rtp[2] << 8) | rtp[3];
    if (seq == -1)
      seq = nseq;
    else if (((seq + 1) & 0xffff) != nseq) {
      unc += (len / 188) * (uint32_t)((uint16_t)nseq-(uint16_t)(seq+1));
      tvhtrace(LS_IPTV, "RTP discontinuity (%i != %i)", seq + 1, nseq);
    }
    seq = nseq;

    /* Move data */
    sbuf_append(&im->mm_iptv_buffer, rtp + hlen, len);
    res += len;
  }

  im->mm_iptv_rtp_seq = seq;
  if (im->mm_active)
    atomic_add(&im->mm_active->tii_stats.unc, unc);

  return res;
}
コード例 #23
0
ファイル: sbuf.c プロジェクト: mephi42/fssh
int sbuf_append_string(struct sbuf *sbuf, const char *s)
{
	return sbuf_append(sbuf, s, strlen(s));
}