2013-05-23 02:05:19 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2013 Guillem Jover <guillem@hadrons.org>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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 <sys/wait.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
#define skip(msg) \
|
|
|
|
do { \
|
|
|
|
printf("skip: %s\n", (msg)); \
|
|
|
|
return; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define DATA_LINES 3
|
|
|
|
|
|
|
|
static const char *data_ascii[] = {
|
|
|
|
"this represents an ascii sequence of lines to test the\n",
|
|
|
|
"fgetln() family of functions\n",
|
|
|
|
"last line without an ending newline",
|
|
|
|
};
|
|
|
|
static const wchar_t *data_wide[] = {
|
|
|
|
L"this represénts an utf-8 seqüence of lînes to test the\n",
|
|
|
|
L"«fgetln()» family of functions § but with an extremely long "
|
|
|
|
"line to test that the reallocation logic works fine, and that "
|
|
|
|
"the strings end up being equal…\n",
|
|
|
|
L"last line ☭ without an ‽ ending newline ♥",
|
|
|
|
};
|
|
|
|
|
|
|
|
#define FILE_COUNT 32
|
|
|
|
|
|
|
|
#define LINE_COUNT 2
|
|
|
|
#define LINE_LEN 2
|
|
|
|
|
|
|
|
struct file {
|
|
|
|
FILE *fp;
|
|
|
|
const void **lines;
|
|
|
|
|
|
|
|
const void *got_buf;
|
|
|
|
int got_len;
|
|
|
|
};
|
|
|
|
|
|
|
|
static FILE *
|
|
|
|
pipe_feed(const char *fmt, const void **buf, int buf_nmemb)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
int rc;
|
|
|
|
int pipefd[2];
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
rc = pipe(pipefd);
|
|
|
|
assert(rc >= 0);
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
assert(pid >= 0);
|
|
|
|
|
|
|
|
if (pid == 0) {
|
|
|
|
int line;
|
|
|
|
|
|
|
|
/* Child writes data to pipe. */
|
|
|
|
rc = close(pipefd[0]);
|
|
|
|
assert(rc >= 0);
|
|
|
|
|
|
|
|
fp = fdopen(pipefd[1], "w");
|
|
|
|
assert(fp);
|
|
|
|
|
|
|
|
for (line = 0; line < buf_nmemb; line++) {
|
|
|
|
rc = fprintf(fp, fmt, buf[line]);
|
|
|
|
assert(rc >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = fclose(fp);
|
|
|
|
assert(rc >= 0);
|
|
|
|
|
|
|
|
_exit(0);
|
|
|
|
} else {
|
|
|
|
/* Parent gets a FILE and reads from it. */
|
|
|
|
rc = close(pipefd[1]);
|
|
|
|
assert(rc >= 0);
|
|
|
|
|
|
|
|
fp = fdopen(pipefd[0], "r");
|
|
|
|
assert(fp);
|
|
|
|
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pipe_close(FILE *fp)
|
|
|
|
{
|
|
|
|
fclose(fp);
|
|
|
|
wait(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_fgetln_single(void)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
size_t len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fp = pipe_feed("%s", (const void **)data_ascii, DATA_LINES);
|
|
|
|
for (i = 0; i < DATA_LINES; i++) {
|
|
|
|
char *str = fgetln(fp, &len);
|
|
|
|
|
|
|
|
assert(str);
|
|
|
|
assert(memcmp(str, data_ascii[i], len) == 0);
|
|
|
|
}
|
|
|
|
assert(fgetln(fp, &len) == NULL);
|
|
|
|
pipe_close(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_fgetln_multi(void)
|
|
|
|
{
|
|
|
|
struct file files[FILE_COUNT];
|
|
|
|
int i, l;
|
|
|
|
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
str = strdup("A\n");
|
|
|
|
str[0] += i;
|
|
|
|
|
2014-11-03 23:21:52 +01:00
|
|
|
files[i].lines = reallocarray(NULL, LINE_COUNT, sizeof(char *));
|
2013-05-23 02:05:19 +02:00
|
|
|
files[i].lines[0] = str;
|
|
|
|
files[i].lines[1] = str;
|
|
|
|
files[i].fp = pipe_feed("%s", files[i].lines, LINE_COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (l = 0; l < LINE_COUNT; l++) {
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
size_t len;
|
|
|
|
char *str;
|
|
|
|
|
|
|
|
str = fgetln(files[i].fp, &len);
|
|
|
|
|
|
|
|
assert(str);
|
|
|
|
assert(len == LINE_LEN);
|
|
|
|
|
|
|
|
files[i].got_len = len;
|
|
|
|
files[i].got_buf = str;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
assert(memcmp(files[i].lines[l], files[i].got_buf,
|
|
|
|
files[i].got_len) == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < LINE_COUNT; i++)
|
|
|
|
pipe_close(files[i].fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_fgetwln_single(void)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
size_t len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fp = pipe_feed("%ls", (const void **)data_wide, DATA_LINES);
|
|
|
|
for (i = 0; i < DATA_LINES; i++) {
|
|
|
|
wchar_t *wstr;
|
|
|
|
|
|
|
|
wstr = fgetwln(fp, &len);
|
|
|
|
assert(wstr);
|
|
|
|
|
|
|
|
assert(wmemcmp(data_wide[i], wstr, len) == 0);
|
|
|
|
}
|
|
|
|
assert(fgetwln(fp, &len) == NULL);
|
|
|
|
pipe_close(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_fgetwln_multi(void)
|
|
|
|
{
|
|
|
|
struct file files[FILE_COUNT];
|
|
|
|
int i, l;
|
|
|
|
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
wchar_t *wstr;
|
|
|
|
|
|
|
|
wstr = wcsdup(L"A\n");
|
|
|
|
wstr[0] += i;
|
|
|
|
|
2014-11-03 23:21:52 +01:00
|
|
|
files[i].lines = reallocarray(NULL, LINE_COUNT, sizeof(char *));
|
2013-05-23 02:05:19 +02:00
|
|
|
files[i].lines[0] = wstr;
|
|
|
|
files[i].lines[1] = wstr;
|
|
|
|
files[i].fp = pipe_feed("%ls", files[i].lines, LINE_COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (l = 0; l < LINE_COUNT; l++) {
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
size_t len;
|
|
|
|
wchar_t *wstr;
|
|
|
|
|
|
|
|
wstr = fgetwln(files[i].fp, &len);
|
|
|
|
|
|
|
|
assert(wstr);
|
|
|
|
assert(len == LINE_LEN);
|
|
|
|
|
|
|
|
files[i].got_len = len;
|
|
|
|
files[i].got_buf = wstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < FILE_COUNT; i++) {
|
|
|
|
assert(wmemcmp(files[i].lines[l], files[i].got_buf,
|
|
|
|
files[i].got_len) == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < LINE_COUNT; i++)
|
|
|
|
pipe_close(files[i].fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_fgetwln(void)
|
|
|
|
{
|
|
|
|
if (setlocale(LC_ALL, "C.UTF-8") == NULL &&
|
|
|
|
setlocale(LC_ALL, "en_US.UTF-8") == NULL)
|
|
|
|
skip("no default UTF-8 locale found");
|
|
|
|
|
|
|
|
test_fgetwln_single();
|
|
|
|
test_fgetwln_multi();
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
test_fgetln_single();
|
|
|
|
test_fgetln_multi();
|
|
|
|
test_fgetwln();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|