SLIP works properly now
Preparation for modem phonebook files
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
set(net_sources)
|
set(net_sources)
|
||||||
list(APPEND net_sources network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c
|
list(APPEND net_sources network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c
|
||||||
net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c
|
net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c net_null.c
|
||||||
net_eeprom_nmc93cxx.c net_tulip.c net_rtl8139.c net_l80225.c net_modem.c)
|
net_eeprom_nmc93cxx.c net_tulip.c net_rtl8139.c net_l80225.c net_modem.c utils/getline.c)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(SLIRP REQUIRED IMPORTED_TARGET slirp)
|
pkg_check_modules(SLIRP REQUIRED IMPORTED_TARGET slirp)
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
|
||||||
|
/* TODO: SLIP support. */
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -14,6 +17,7 @@
|
|||||||
#include <86box/fifo8.h>
|
#include <86box/fifo8.h>
|
||||||
#include <86box/timer.h>
|
#include <86box/timer.h>
|
||||||
#include <86box/serial.h>
|
#include <86box/serial.h>
|
||||||
|
#include <86box/plat.h>
|
||||||
#include <86box/network.h>
|
#include <86box/network.h>
|
||||||
#include <86box/plat_unused.h>
|
#include <86box/plat_unused.h>
|
||||||
|
|
||||||
@@ -48,6 +52,18 @@ typedef enum modem_mode_t
|
|||||||
MODEM_MODE_DATA = 1
|
MODEM_MODE_DATA = 1
|
||||||
} modem_mode_t;
|
} modem_mode_t;
|
||||||
|
|
||||||
|
typedef enum modem_slip_stage_t
|
||||||
|
{
|
||||||
|
MODEM_SLIP_STAGE_USERNAME,
|
||||||
|
MODEM_SLIP_STAGE_PASSWORD
|
||||||
|
} modem_slip_stage_t;
|
||||||
|
|
||||||
|
typedef struct modem_phonebook_entry_t
|
||||||
|
{
|
||||||
|
char phone[1024];
|
||||||
|
char address[1024];
|
||||||
|
} modem_phonebook_entry_t;
|
||||||
|
|
||||||
typedef struct modem_t
|
typedef struct modem_t
|
||||||
{
|
{
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
@@ -90,6 +106,9 @@ typedef struct modem_t
|
|||||||
bool recCommand;
|
bool recCommand;
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
} telClient;
|
} telClient;
|
||||||
|
|
||||||
|
modem_phonebook_entry_t entries[20];
|
||||||
|
uint32_t entries_num;
|
||||||
|
|
||||||
netcard_t *card;
|
netcard_t *card;
|
||||||
} modem_t;
|
} modem_t;
|
||||||
@@ -107,6 +126,43 @@ static modem_t *instance;
|
|||||||
|
|
||||||
static void modem_do_command(modem_t* modem);
|
static void modem_do_command(modem_t* modem);
|
||||||
|
|
||||||
|
extern ssize_t local_getline(char **buf, size_t *bufsiz, FILE *fp);
|
||||||
|
|
||||||
|
static void
|
||||||
|
modem_read_phonebook_file(modem_t* modem, const char* path)
|
||||||
|
{
|
||||||
|
FILE* file = plat_fopen(path, "r");
|
||||||
|
char* buf = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
modem->entries_num = 0;
|
||||||
|
|
||||||
|
while (local_getline(&buf, &size, file) != -1) {
|
||||||
|
modem_phonebook_entry_t entry = { { 0 }, { 0 } };
|
||||||
|
int res = 0;
|
||||||
|
buf[strcspn(buf, "\r\n")] = 0;
|
||||||
|
|
||||||
|
res = sscanf(buf, "%s %s", entry.phone, entry.address);
|
||||||
|
|
||||||
|
if (res == 0 || res == 1) {
|
||||||
|
/* Appears to be a bad line. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strspn(entry.phone, "01234567890*=,;#+>") != strlen(entry.phone)) {
|
||||||
|
/* Invalid characters. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
modem->entries[modem->entries_num++] = entry;
|
||||||
|
if (modem->entries_num >= 20)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
modem_echo(modem_t* modem, uint8_t c)
|
modem_echo(modem_t* modem, uint8_t c)
|
||||||
{
|
{
|
||||||
@@ -190,6 +246,8 @@ process_tx_packet(modem_t *modem, uint8_t *p, uint32_t len)
|
|||||||
uint8_t *processed_tx_packet = calloc(len, 1);
|
uint8_t *processed_tx_packet = calloc(len, 1);
|
||||||
uint8_t c = 0;
|
uint8_t c = 0;
|
||||||
|
|
||||||
|
pclog("Processing SLIP packet of %u bytes\n", len);
|
||||||
|
|
||||||
while (pos < len) {
|
while (pos < len) {
|
||||||
c = p[pos];
|
c = p[pos];
|
||||||
pos++;
|
pos++;
|
||||||
@@ -223,7 +281,18 @@ process_tx_packet(modem_t *modem, uint8_t *p, uint32_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
send_tx_packet:
|
send_tx_packet:
|
||||||
network_tx(modem->card, processed_tx_packet, received);
|
if (received)
|
||||||
|
{
|
||||||
|
uint8_t* buf = calloc(received + 14, 1);
|
||||||
|
buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = 0xFF;
|
||||||
|
buf[6] = buf[7] = buf[8] = buf[9] = buf[10] = buf[11] = 0xFC;
|
||||||
|
buf[12] = 0x08;
|
||||||
|
buf[13] = 0x00;
|
||||||
|
memcpy(buf + 14, processed_tx_packet, received);
|
||||||
|
network_tx(modem->card, buf, received + 14);
|
||||||
|
free(processed_tx_packet);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +336,6 @@ host_to_modem_cb(void *priv)
|
|||||||
|
|
||||||
if (modem->mode == MODEM_MODE_DATA && fifo8_num_used(&modem->rx_data)) {
|
if (modem->mode == MODEM_MODE_DATA && fifo8_num_used(&modem->rx_data)) {
|
||||||
serial_write_fifo(modem->serial, fifo8_pop(&modem->rx_data));
|
serial_write_fifo(modem->serial, fifo8_pop(&modem->rx_data));
|
||||||
fprintf(stderr, "(data)\n");
|
|
||||||
} else if (fifo8_num_used(&modem->data_pending)) {
|
} else if (fifo8_num_used(&modem->data_pending)) {
|
||||||
uint8_t val = fifo8_pop(&modem->data_pending);
|
uint8_t val = fifo8_pop(&modem->data_pending);
|
||||||
serial_write_fifo(modem->serial, val);
|
serial_write_fifo(modem->serial, val);
|
||||||
@@ -326,11 +394,15 @@ modem_write(UNUSED(serial_t *s), void *priv, uint8_t txval)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void modem_send_res(modem_t* modem, const ResTypes response) {
|
void modem_send_res(modem_t* modem, const ResTypes response) {
|
||||||
|
char response_str_connect[256] = { 0 };
|
||||||
const char* response_str = NULL;
|
const char* response_str = NULL;
|
||||||
uint32_t code = -1;
|
uint32_t code = -1;
|
||||||
|
|
||||||
|
snprintf(response_str_connect, sizeof(response_str_connect), "CONNECT %u", modem->baudrate);
|
||||||
|
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case ResOK: code = 0; response_str = "OK"; break;
|
case ResOK: code = 0; response_str = "OK"; break;
|
||||||
case ResCONNECT: code = 1; response_str = "CONNECT 33600"; break;
|
case ResCONNECT: code = 1; response_str = response_str_connect; break;
|
||||||
case ResRING: code = 2; response_str = "RING"; break;
|
case ResRING: code = 2; response_str = "RING"; break;
|
||||||
case ResNOCARRIER: code = 3; response_str = "NO CARRIER"; break;
|
case ResNOCARRIER: code = 3; response_str = "NO CARRIER"; break;
|
||||||
case ResERROR: code = 4; response_str = "ERROR"; break;
|
case ResERROR: code = 4; response_str = "ERROR"; break;
|
||||||
@@ -416,9 +488,13 @@ void
|
|||||||
modem_dial(modem_t* modem, const char* str)
|
modem_dial(modem_t* modem, const char* str)
|
||||||
{
|
{
|
||||||
/* TODO: Port TCP/IP support from DOSBox. */
|
/* TODO: Port TCP/IP support from DOSBox. */
|
||||||
if (!strncmp(str, "0.0.0.0", sizeof("0.0.0.0") - 1)) {
|
if (!strncmp(str, "0.0.0.0", sizeof("0.0.0.0") - 1))
|
||||||
|
{
|
||||||
|
pclog("Turning on SLIP\n");
|
||||||
modem_enter_connected_state(modem);
|
modem_enter_connected_state(modem);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
modem_send_res(modem, ResNOCARRIER);
|
modem_send_res(modem, ResNOCARRIER);
|
||||||
modem_enter_idle_state(modem);
|
modem_enter_idle_state(modem);
|
||||||
}
|
}
|
||||||
@@ -779,19 +855,31 @@ fifo8_resize_2x(Fifo8* fifo)
|
|||||||
static int
|
static int
|
||||||
modem_rx(void *priv, uint8_t *buf, int io_len)
|
modem_rx(void *priv, uint8_t *buf, int io_len)
|
||||||
{
|
{
|
||||||
|
#if 1
|
||||||
modem_t* modem = (modem_t*)priv;
|
modem_t* modem = (modem_t*)priv;
|
||||||
uint8_t c = 0;
|
uint8_t c = 0;
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
|
|
||||||
if (!modem->connected) {
|
if (!modem->connected) {
|
||||||
/* Drop packet. */
|
/* Drop packet. */
|
||||||
|
pclog("Dropping %d bytes\n", io_len - 14);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((io_len) <= (fifo8_num_free(&modem->rx_data) / 2)) {
|
if ((io_len) >= (fifo8_num_free(&modem->rx_data))) {
|
||||||
fifo8_resize_2x(&modem->rx_data);
|
fifo8_resize_2x(&modem->rx_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(buf[12] == 0x08 && buf[13] == 0x00)) {
|
||||||
|
pclog("Dropping %d bytes (non-IP packet (ethtype 0x%02X%02X))\n", io_len - 14, buf[12], buf[13]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pclog("Receiving %d bytes\n", io_len - 14);
|
||||||
|
/* Strip the Ethernet header. */
|
||||||
|
io_len -= 14;
|
||||||
|
buf += 14;
|
||||||
|
|
||||||
fifo8_push(&modem->rx_data, END);
|
fifo8_push(&modem->rx_data, END);
|
||||||
for (i = 0; i < io_len; i++) {
|
for (i = 0; i < io_len; i++) {
|
||||||
switch (buf[i]) {
|
switch (buf[i]) {
|
||||||
@@ -810,6 +898,7 @@ modem_rx(void *priv, uint8_t *buf, int io_len)
|
|||||||
}
|
}
|
||||||
fifo8_push(&modem->rx_data, END);
|
fifo8_push(&modem->rx_data, END);
|
||||||
return 1;
|
return 1;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -37,6 +37,7 @@
|
|||||||
#include <86box/ini.h>
|
#include <86box/ini.h>
|
||||||
#include <86box/config.h>
|
#include <86box/config.h>
|
||||||
#include <86box/video.h>
|
#include <86box/video.h>
|
||||||
|
#include <86box/bswap.h>
|
||||||
|
|
||||||
#define _SSIZE_T_DEFINED
|
#define _SSIZE_T_DEFINED
|
||||||
#include <slirp/libslirp.h>
|
#include <slirp/libslirp.h>
|
||||||
@@ -76,6 +77,29 @@ typedef struct net_slirp_t {
|
|||||||
#endif
|
#endif
|
||||||
} net_slirp_t;
|
} net_slirp_t;
|
||||||
|
|
||||||
|
/* Pulled off from libslirp code. This is only needed for modem. */
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct arphdr_local {
|
||||||
|
unsigned char h_dest[6]; /* destination eth addr */
|
||||||
|
unsigned char h_source[6]; /* source ether addr */
|
||||||
|
unsigned short h_proto; /* packet type ID field */
|
||||||
|
|
||||||
|
unsigned short ar_hrd; /* format of hardware address */
|
||||||
|
unsigned short ar_pro; /* format of protocol address */
|
||||||
|
unsigned char ar_hln; /* length of hardware address */
|
||||||
|
unsigned char ar_pln; /* length of protocol address */
|
||||||
|
unsigned short ar_op; /* ARP opcode (command) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ethernet looks like this : This bit is variable sized however...
|
||||||
|
*/
|
||||||
|
uint8_t ar_sha[6]; /* sender hardware address */
|
||||||
|
uint32_t ar_sip; /* sender IP address */
|
||||||
|
uint8_t ar_tha[6]; /* target hardware address */
|
||||||
|
uint32_t ar_tip; /* target IP address */
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
#ifdef ENABLE_SLIRP_LOG
|
#ifdef ENABLE_SLIRP_LOG
|
||||||
int slirp_do_log = ENABLE_SLIRP_LOG;
|
int slirp_do_log = ENABLE_SLIRP_LOG;
|
||||||
|
|
||||||
@@ -455,6 +479,32 @@ net_slirp_init(const netcard_t *card, const uint8_t *mac_addr, UNUSED(void *priv
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
slirp->sock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
slirp->sock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!strcmp(network_card_get_internal_name(net_cards_conf[net_card_current].device_num), "modem")) {
|
||||||
|
/* Send a gratuitous ARP here to make SLiRP work properly with SLIP connections. */
|
||||||
|
struct arphdr_local arphdr;
|
||||||
|
/* ARP part. */
|
||||||
|
arphdr.ar_hrd = bswap16(1);
|
||||||
|
arphdr.ar_pro = bswap16(0x0800);
|
||||||
|
arphdr.ar_hln = 6;
|
||||||
|
arphdr.ar_pln = 4;
|
||||||
|
arphdr.ar_op = bswap16(1);
|
||||||
|
memcpy(&arphdr.ar_sha, mac_addr, 6);
|
||||||
|
memcpy(&arphdr.ar_tha, mac_addr, 6);
|
||||||
|
arphdr.ar_sip = dhcp.s_addr;
|
||||||
|
arphdr.ar_tip = dhcp.s_addr;
|
||||||
|
|
||||||
|
/* Ethernet header part. */
|
||||||
|
arphdr.h_proto = bswap16(0x0806);
|
||||||
|
memset(arphdr.h_dest, 0xff, 6);
|
||||||
|
memset(arphdr.h_source, 0x52, 6);
|
||||||
|
arphdr.h_source[2] = 0x0a;
|
||||||
|
arphdr.h_source[3] = 0x00;
|
||||||
|
arphdr.h_source[4] = slirp_card_num;
|
||||||
|
arphdr.h_source[5] = 2;
|
||||||
|
slirp_input(slirp->slirp, (const uint8_t *)&arphdr, sizeof(struct arphdr_local));
|
||||||
|
}
|
||||||
|
|
||||||
slirp_log("SLiRP: creating thread...\n");
|
slirp_log("SLiRP: creating thread...\n");
|
||||||
slirp->poll_tid = thread_create(net_slirp_thread, slirp);
|
slirp->poll_tid = thread_create(net_slirp_thread, slirp);
|
||||||
|
|
||||||
|
@@ -454,6 +454,7 @@ netcard_t *
|
|||||||
network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETSETLINKSTATE set_link_state)
|
network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETSETLINKSTATE set_link_state)
|
||||||
{
|
{
|
||||||
netcard_t *card = calloc(1, sizeof(netcard_t));
|
netcard_t *card = calloc(1, sizeof(netcard_t));
|
||||||
|
int net_type = net_cards_conf[net_card_current].net_type;
|
||||||
card->queued_pkt.data = calloc(1, NET_MAX_FRAME);
|
card->queued_pkt.data = calloc(1, NET_MAX_FRAME);
|
||||||
card->card_drv = card_drv;
|
card->card_drv = card_drv;
|
||||||
card->rx = rx;
|
card->rx = rx;
|
||||||
@@ -470,7 +471,12 @@ network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETSETLINKSTATE set_lin
|
|||||||
network_queue_init(&card->queues[i]);
|
network_queue_init(&card->queues[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (net_cards_conf[net_card_current].net_type) {
|
if (!strcmp(network_card_get_internal_name(net_cards_conf[net_card_current].device_num), "modem") && net_type >= NET_TYPE_PCAP) {
|
||||||
|
/* Force SLiRP here. Modem only operates on non-Ethernet frames. */
|
||||||
|
net_type = NET_TYPE_SLIRP;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (net_type) {
|
||||||
case NET_TYPE_SLIRP:
|
case NET_TYPE_SLIRP:
|
||||||
card->host_drv = net_slirp_drv;
|
card->host_drv = net_slirp_drv;
|
||||||
card->host_drv.priv = card->host_drv.init(card, mac, NULL, net_drv_error);
|
card->host_drv.priv = card->host_drv.init(card, mac, NULL, net_drv_error);
|
||||||
|
81
src/network/utils/getline.c
Normal file
81
src/network/utils/getline.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2011 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Christos Zoulas.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
local_getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
|
||||||
|
{
|
||||||
|
char *ptr, *eptr;
|
||||||
|
|
||||||
|
|
||||||
|
if (*buf == NULL || *bufsiz == 0) {
|
||||||
|
*bufsiz = BUFSIZ;
|
||||||
|
if ((*buf = malloc(*bufsiz)) == NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
|
||||||
|
int c = fgetc(fp);
|
||||||
|
if (c == -1) {
|
||||||
|
if (feof(fp)) {
|
||||||
|
ssize_t diff = (ssize_t)(ptr - *buf);
|
||||||
|
if (diff != 0) {
|
||||||
|
*ptr = '\0';
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*ptr++ = c;
|
||||||
|
if (c == delimiter) {
|
||||||
|
*ptr = '\0';
|
||||||
|
return ptr - *buf;
|
||||||
|
}
|
||||||
|
if (ptr + 2 >= eptr) {
|
||||||
|
char *nbuf;
|
||||||
|
size_t nbufsiz = *bufsiz * 2;
|
||||||
|
ssize_t d = ptr - *buf;
|
||||||
|
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
|
||||||
|
return -1;
|
||||||
|
*buf = nbuf;
|
||||||
|
*bufsiz = nbufsiz;
|
||||||
|
eptr = nbuf + nbufsiz;
|
||||||
|
ptr = nbuf + d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
local_getline(char **buf, size_t *bufsiz, FILE *fp)
|
||||||
|
{
|
||||||
|
return local_getdelim(buf, bufsiz, '\n', fp);
|
||||||
|
}
|
Reference in New Issue
Block a user