From patchwork Wed Jun 18 14:33:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jovan Dmitrovic X-Patchwork-Id: 114672 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7360C381621C for ; Wed, 18 Jun 2025 14:35:14 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from DB3PR0202CU003.outbound.protection.outlook.com (mail-northeuropeazlp170100001.outbound.protection.outlook.com [IPv6:2a01:111:f403:c200::1]) by sourceware.org (Postfix) with ESMTPS id 55A1C3871880 for ; Wed, 18 Jun 2025 14:34:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 55A1C3871880 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=htecgroup.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=htecgroup.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 55A1C3871880 Authentication-Results: server2.sourceware.org; arc=pass smtp.remote-ip=2a01:111:f403:c200::1 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1750257250; cv=pass; b=dEBNUDoYc3M6HjxgLZEcL1dGZLVdp6Xq410K+SVmmeOdBqMxsTsQn1nqiNX7kaxV01o6bacVZ2iTxZo+ZQXPleuR5AjgwbvbBxD6xGFkGKUyesIbLf0rOvb1ofsrIxl+6kWht1u8YJrwILpZvFA+Mj+0oewNwYETCZ6WOjwqxzw= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1750257250; c=relaxed/simple; bh=0YoFfmwV1GfM2EcFuw5RhV1BYoACicDrRadWPMoNqI0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=GJ1zS8B/Pvj7FsucbNaq10n8zkcDAByegqJxMjZopDzKP0VF5omHcGSu95830t2p5l1CNGuTcYARcOvjn/bbXCmJgA9uBuypmv4i+E3iz/q+HngyN4vmzzxpC6WyNtOYja72coBM9ARSyOYtkzeu1jzV4lhqZi286ncfvXngZ1E= ARC-Authentication-Results: i=2; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 55A1C3871880 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=htecgroup.com header.i=@htecgroup.com header.a=rsa-sha256 header.s=selector1 header.b=TKd+ycXO ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=CjWzG0LK+SvdJZsi2BQTc2dmf0vf+f2mbZUURSaurVl9Aull8W39nT3s3uAOQzIC9swwR3qsmd7kO15pc0J1gFq9lmx7iR6EYtqnj3iTKtA3BHDXkV9mM10NtZ7TXBfoSCPHkU1YDqtjmfGsFEyKNlev7SBrVkfVXKxh/ykSmqQ7NTIg41OAmnZHly0yaZGLdN6IKyCw1ku7JbxP0ZxTIbEhHz9yztdXLx41Epsg/tAIC5g4LPz6ut6w6RzJesUtwoprThhCRWcL1rwitgNUChoMxazkX0PbgNyveGBwvCXm3AKxJvKD+RXo5u/AdNCfXA6Xlf3AfNVMEUtjBV1y0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=0YoFfmwV1GfM2EcFuw5RhV1BYoACicDrRadWPMoNqI0=; b=ip+VEAac5doIFVWcxIHiplzn+7zj4IPmmbBJNQ+qYO8DPV+qKuAjN+H0XXAtnVeRnRYu7fzAi6Wem10ODp0iuW/etEuieIFQP5MFivhvy+aCCARWiPWJYFvW+cXSWZ9Cj1p44mXn4keZdH1pt5yj0u8M/UziULM6fHguywwaZ4sN20g4k8A2rupa13dccg5q3Qi3kxkMC3zfIARZ93RCLQ1cWv0kgDQNXoippBRLyUcef2DALsQwJgkTQdj7H8a4sWKncxrYt1Dv1CGdoNy4xSEBQC+K/heJrclH0HaQFRTJt+07GSQlr8R/EsSwxD7VqyDhaRmcCuo//RhrOVSzFQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=htecgroup.com; dmarc=pass action=none header.from=htecgroup.com; dkim=pass header.d=htecgroup.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=htecgroup.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=0YoFfmwV1GfM2EcFuw5RhV1BYoACicDrRadWPMoNqI0=; b=TKd+ycXOo3gFeKHc8QynkemNjNOq05SCrnkn340OdjMfFKfyIkdixZ6sqGA3hbJUNwel8X58fa0ow78D4VqdZB8nAckbBm5YepI8gT0eZRJjYxzyIIDI4NKSOZWn6WaSsl6RnTB5VKpKVZ4WPIOQjwbzjR/F3BjIqq6UrvRvsnoXyMblpwWcy+XZdKoxFbM/EO7WLTSe2lF19Khk4/lNqvLH3pQF2AcyHQXhp2Y1T5UxOBchpw0CVDXFxSKN8Q/+GJveEorA7HgN290Vl+K+ql3Sa3zK2H2HUW8E2ZwM5j5S4nMNRiv5ntw+9JI6v04rLSllE1P3G4Su4njz3GNvAw== Received: from PAVPR09MB6451.eurprd09.prod.outlook.com (2603:10a6:102:304::13) by PAWPR09MB6834.eurprd09.prod.outlook.com (2603:10a6:102:386::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8835.29; Wed, 18 Jun 2025 14:33:38 +0000 Received: from PAVPR09MB6451.eurprd09.prod.outlook.com ([fe80::4569:9af3:a4cf:48d]) by PAVPR09MB6451.eurprd09.prod.outlook.com ([fe80::4569:9af3:a4cf:48d%5]) with mapi id 15.20.8835.027; Wed, 18 Jun 2025 14:33:38 +0000 From: Jovan Dmitrovic To: "libc-alpha@sourceware.org" CC: Djordje Todorovic , Joseph Myers , Adhemerval Zanella Netto , DJ Delorie , Jovan Dmitrovic Subject: [PATCH v2 1/8] stdio: Add more setvbuf tests Thread-Topic: [PATCH v2 1/8] stdio: Add more setvbuf tests Thread-Index: AQHb4F36ss43Ua7dMUKawLBWoKxfqA== Date: Wed, 18 Jun 2025 14:33:38 +0000 Message-ID: <20250618143327.2665823-2-jovan.dmitrovic@htecgroup.com> References: <20250618143327.2665823-1-jovan.dmitrovic@htecgroup.com> In-Reply-To: <20250618143327.2665823-1-jovan.dmitrovic@htecgroup.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=htecgroup.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: PAVPR09MB6451:EE_|PAWPR09MB6834:EE_ x-ms-office365-filtering-correlation-id: c46bbf76-afd0-4f53-75f9-08ddae751d4e x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|1800799024|366016|376014|38070700018; x-microsoft-antispam-message-info: =?utf-8?q?p+5ZhRMgzBqjZEzDC7WHak4f63SZi1P?= =?utf-8?q?wUKV+BeAbncVbZG681ocADfOzagqKs9JOF+vltttxgh0M2o8WvD7uEXG9b4BUFy3f?= =?utf-8?q?ev3Zj0coE7bpqWI3g3w3qL8iAxWAuZ5lds8XMpc3DiecwoJjmj4VmlNm+qzFMVYbA?= =?utf-8?q?kI9yDeA2paEy2c85M6wHIqVJ26lTKIHVsrM8/AyW0PmCWVAHqJnsaQIKpCpCCnQDV?= =?utf-8?q?SpraBLqCq7P+YpK+otiBqTw2+o+IJaywlD4ITYtcy3mTzk8vA8rkqQvAUExFzBzNL?= =?utf-8?q?9w+qVhYrNjBFefui7lHsgUF7loNOq1xunvAh5w6H7STQ28RhHChhzcY7GQR6smbcw?= =?utf-8?q?vTVomckAo4kqOduMNTx+X+A+AO2zuXuizciuxzIW3NCHmvgFSt9tdLoUs8DcnM0pz?= =?utf-8?q?LY6msxlIU8Rkpyd2NV2HFVQIt1wZ3wF+5MPXnTNFgnVBs47WLasne1F96p1qzvokJ?= =?utf-8?q?wduGiM+olRZZhPi9LPgd3jszp6SLJ3Vcb0uAGHYxdaJ+SVuyLi5AiKnNk5sBPw0je?= =?utf-8?q?a7yIhb6vqlbfl2IY89T44J0mli3uwXOV1hbzVJWZNI0WEiMgg7OprH5NlKPwBw3Lv?= =?utf-8?q?UgkivWJPr2ua5VOV8R49HK/ried9V42kAeu4N29KACBDpnxldwc49CM3mJ+fdvSzN?= =?utf-8?q?drQuvAKocsjzWyixTAFl+GuqlVJEIx7N4c204ccHvzdiyzFp8EeufgSrONdRh6HML?= =?utf-8?q?bv1f/Mp1dk6nU5o3XYZMTMcS9+CP6SNfXgY0MBN/EezkZbdfh/sqry2cg0oQIsnb+?= =?utf-8?q?IAIr2h06qZ+eOMa3xi1TePOkD3FbuDrJf0BqY0Iy02RpbFThoA3hc0lI081tqvMpU?= =?utf-8?q?kJbmyYMqXis+CBkxIHN1TGF5An2O+txONzEYMhKWy/gyK9vvgwxtkNLN6Eam35vzS?= =?utf-8?q?5dODbq7VBOa4raFM3gAANwMJLYlFW90srUmcvKRlmtY/zwSsbCnl4itII2i5xhNhx?= =?utf-8?q?93JkmKak7Ssr3xUovamyLyw/uF0Xh1RrDdkpsN4s5xdkbtvAUSrsXY4wmB71IkUbj?= =?utf-8?q?FNG7Bj/5kXqumGMc32s+ue6IxYqhnWRK5Yd1P2/8LjCWBwDP/8xgAlK2F9QbSqe6z?= =?utf-8?q?M8WuNRpBVnjhuuOgPJSpGf9AFkVt6VPb5c6UwOodqgOl0uyL9fUGZhWa0f2PiZz8I?= =?utf-8?q?gv4qmo21/P5Z3gQ1uKyiTjsE1Ar04F9QSc1H08EE/PmsglgXrg7RG5rwPeQpPVdgo?= =?utf-8?q?ACxSzQrskNhk+9QKVpqVvmGFswpOolm2r28XDEDEzEirEmOcuZxdFtHVHRqb8zmXH?= =?utf-8?q?zmxGJU5ZkGjcj3jGY97SyGOqpd7CQSuaKZQ+l2e4rHDb1dzy6vDaGmtUYa228YKUD?= =?utf-8?q?OkNcx/OHkoytid7QnpP6bXKqlpg29Ygn8o3wBbfM42M6g9EqyYe3l/f5Dtu76Uq5O?= =?utf-8?q?Ll2B3B/99nm8V3XgKD2muh6d7EZ28gwsw=3D=3D?= x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:PAVPR09MB6451.eurprd09.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(1800799024)(366016)(376014)(38070700018); DIR:OUT; SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?K/pWGNmD880LVr+5qOg+SRl81WBc?= =?utf-8?q?7xGktbohLxpVDSHby/+od/Z0IqWaaGBw9FjlSkzdXzXwe+hdDhr3ITOXcEXFAx+Mv?= =?utf-8?q?4F0+SJBqhRHz35vKmwB8Xukr9SMDOGewuZGhE+39MQE17t/LBaWytzC0E5suTtVEi?= =?utf-8?q?77i2prtYiM4/si7kq2YlDTvKjwSbOoqnyF+km9d+s1/CVqr22iijagK4LBeYAu+hZ?= =?utf-8?q?EGLtKZQwi4jo+pIBpONsWzykmjVrTXEkp753kb/7eJxhLjwMipChf+KgI8pV3tuyk?= =?utf-8?q?NDpgLkawrMJkRj8zWaayI2EFE8vipIbIjyBzZrPczC8Zi93A/ssybXr1yCLJFao73?= =?utf-8?q?RJzwihbXSXhJ8JIMp1sLSmcZchgv0Wkfsey4PZbXlI1x8JsjOTXq036h0L1es3+yu?= =?utf-8?q?BSr6KSFe6XF+Nf+3eadZph06MLSJcLGZQydF7pM3pe8jKulaBFxg5sJ1t57gemYsu?= =?utf-8?q?hd3CLXI2GO7ClKdwRShMu5e+sP2uMoKowvnQj9GqyDtc5RrAFQWNm6HLACfQau8W/?= =?utf-8?q?fDvd8mJ2EhHTavJTJmIq5EJkLq2UswKNDVA/i7GbvkW1SoYDBL0CCaGFl6Fyl3kjJ?= =?utf-8?q?eK2T3ZlAyot1XtHUYNRQ2EH+atDbmOtM3wNiJVxlKTiYtvqG6rTPd/whhpwIROpj+?= =?utf-8?q?6slJ2RZzpJogvwivXaaqDlDyd05OalrXiuZ6vyfAqcRqrj7yQS0Sm9tszIMTIKwKe?= =?utf-8?q?9oc5Cqvvr1xY1WpNvFevxAg4/inEg3mRLT139ISAzMd1pruBvEyra33ivoaN32/5M?= =?utf-8?q?z3RtA1IRo+KaW5wOa7UC2D4DgBmdl6WfvlPkedhUlU4hdMbciUC48KXSy97SgEDnY?= =?utf-8?q?Q/pIYgCx/41y2KyURlb4zObTn1Iq3ADYePlserSQL+/dXoHyAYRpPB40+3bsUoYm7?= =?utf-8?q?H1o0/QXvTG6rNX4sA4n3GBxRCz+kZ+fGAzCunoU/TEWUVMaEW0Pvw670vMCnpo7wo?= =?utf-8?q?7MUHLbKgTxjWoc5aJc8VsAK6NjDfLQYwF4H5cwRpKUFuvgIZysWAKYSx9zhmYXvK6?= =?utf-8?q?aoF1yAo2H8iZv1gYgkKdrEDVwiWtn+oSIacYebYrqG1KH1b1gcuzY/WlO3DGgq/bb?= =?utf-8?q?RtDQxM2b/AtljXbQzuIC+5EUja/zmXcavemqZHkVn9NLey5KyFZFqgr230QmqWMso?= =?utf-8?q?VGQJdRF2YJ4YZsIFK5E1gvrD7v5oeueePY5RhBJe4J3khjfpQFw4xEWjU/AWxWgAD?= =?utf-8?q?769tYv3AdWyxLW77s0UnmXBkk+1Wz+fZCCUPOTPscATJX511crXu9ahvTdCDtiO8N?= =?utf-8?q?+LBXgMYPOJ613pmVhJbtJ/YSt2exj4BLpSaCdoy1e1w8bcVh9p5uVRmZaYt+9otaP?= =?utf-8?q?OASVVi1cWeE1vdpmZfjRApt7rLjrLamcoZW4/yq+12CVWVEb8VSh5Gv5vVO3ExRB1?= =?utf-8?q?aEawLGEtxZUdBm1/E3/4VDKNWeMKhzNK7KsV+7ObMfYr0voY11BkDqD6cd/242+Xh?= =?utf-8?q?v6+KYVhCenpQrM5Vc1+PK+KY89ohU/0+0mIxYYy2j4KIll72wvVBu+/kWOsoqJ3SY?= =?utf-8?q?eGK4Trtu1zN9IQfQrXsdm2jWX8OPeqndQaOwTWWhvNij/J01qG5UBR4=3D?= Content-ID: MIME-Version: 1.0 X-OriginatorOrg: htecgroup.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: PAVPR09MB6451.eurprd09.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: c46bbf76-afd0-4f53-75f9-08ddae751d4e X-MS-Exchange-CrossTenant-originalarrivaltime: 18 Jun 2025 14:33:38.2205 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 9f85665b-7efd-4776-9dfe-b6bfda2565ee X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: SnIfOldjB+JAQr9EEwcdRwvmQWljns9ygK6fZMuqOjMSgvmlKvvoezGbg6A2LOMRPtFFWcNQ5lzoXgXXCRBKWyAPPhUn1544ESmyxAqFgso= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PAWPR09MB6834 X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, KAM_SHORT, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org From: DJ Delorie Signed-off-by: Jovan Dmitrović --- stdio-common/Makefile | 10 +- stdio-common/tst-setvbuf2-ind.c | 2 + stdio-common/tst-setvbuf2.c | 1030 +++++++++++++++++++++++++++++++ 3 files changed, 1041 insertions(+), 1 deletion(-) create mode 100644 stdio-common/tst-setvbuf2-ind.c create mode 100644 stdio-common/tst-setvbuf2.c -- 2.34.1 diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 31f40cf57c..3fd33b836d 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -378,7 +378,9 @@ endif endif tests-container += \ - tst-popen3 + tst-popen3 \ + tst-setvbuf2 \ + tst-setvbuf2-ind # tests-container generated += \ @@ -390,6 +392,8 @@ generated += \ tests-internal = \ tst-grouping_iterator \ + tst-setvbuf2 \ + tst-setvbuf2-ind \ # tests-internal test-srcs = \ @@ -762,6 +766,10 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out cmp $^ > $@; \ $(evaluate-test) +CFLAGS-tst-setvbuf2.c += -DIND_PROC=\"$(objpfx)tst-setvbuf2-ind\" +$(objpfx)tst-setvbuf2-ind : $(objpfx)tst-setvbuf2-ind.o +$(objpfx)tst-setvbuf2.out: $(objpfx)tst-setvbuf2-ind + $(objpfx)tst-printf-round: $(libm) $(objpfx)tst-scanf-round: $(libm) diff --git a/stdio-common/tst-setvbuf2-ind.c b/stdio-common/tst-setvbuf2-ind.c new file mode 100644 index 0000000000..fda2942c24 --- /dev/null +++ b/stdio-common/tst-setvbuf2-ind.c @@ -0,0 +1,2 @@ +#define INDEPENDENT_PART 1 +#include "tst-setvbuf2.c" diff --git a/stdio-common/tst-setvbuf2.c b/stdio-common/tst-setvbuf2.c new file mode 100644 index 0000000000..6cc83355f3 --- /dev/null +++ b/stdio-common/tst-setvbuf2.c @@ -0,0 +1,1030 @@ +/* Test setvbuf under various conditions. + Copyright (C) 2025 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This file is used twice, once as the test itself (where do_test + is defined) and once as a subprocess we spawn to test stdin et all + (where main is defined). INDEPENDENT_PART is defined for the + latter. + + Note also that the purpose of this test is to test setvbuf, not the + underlying buffering code. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Dear future developer: If you are reading this, you are likely + trying to change or understand this test. In that case, these + debug/dump macros will be helpful. */ +#if 0 +# define debug printf ("\033[3%dm%s:%d\033[0m\n", \ + (__LINE__ % 6) + 1, __FUNCTION__, __LINE__); + +static void +dumpfp (FILE *fp) +{ + char f[10], *p=f; + + if (fp->_flags & _IO_UNBUFFERED) + *p++ = 'N'; + if (fp->_flags & _IO_LINE_BUF) + *p++ = 'L'; + if (p == f) + *p++ = 'B'; + *p = 0; + + printf ("FILE %p flags %s" + " read %p \033[%dm%+ld \033[%dm%+ld\033[0m" + " write %p \033[%dm%+ld \033[%dm%+ld\033[0m %ld" + " buf %p \033[%dm%+ld\033[0m sz %ld pend %ld\n", + fp, f, + + fp->_IO_read_base, + fp->_IO_read_ptr == fp->_IO_read_base ? 33 : 32, + fp->_IO_read_ptr - fp->_IO_read_base, + fp->_IO_read_end == fp->_IO_read_base ? 33 : 36, + fp->_IO_read_end - fp->_IO_read_base, + + fp->_IO_write_base, + fp->_IO_write_ptr == fp->_IO_write_base ? 33 : 32, + fp->_IO_write_ptr - fp->_IO_write_base, + fp->_IO_write_end == fp->_IO_write_base ? 33 : 36, + fp->_IO_write_end - fp->_IO_write_base, + fp->_IO_write_end - fp->_IO_write_base, + + fp->_IO_buf_base, + fp->_IO_buf_end == fp->_IO_buf_base ? 33 : 35, + fp->_IO_buf_end - fp->_IO_buf_base, + __fbufsize (fp), __fpending (fp) + ); +} +#else +# define debug +# define dumpfp(FP) +#endif + +#ifndef INDEPENDENT_PART +/* st_blksize value for that file, or BUFSIZ if out of range. */ +static int blksize = BUFSIZ; +#endif + +/* Our test buffer. */ +#define TEST_BUFSIZE 42 +static int bufsize = TEST_BUFSIZE < BUFSIZ ? TEST_BUFSIZE : BUFSIZ; +static char *buffer; + +/* Test data, both written to that file and used as an in-memory + stream. */ +char test_data[2 * BUFSIZ]; + +#define TEST_STRING "abcdef\n" + +enum test_source_case + { + test_source_file, + test_source_pipe, + test_source_fifo, + test_source_pseudo_terminal, + test_source_dev_null, + test_source_count, + }; + +static const char *const test_source_name[test_source_count] = + { + "regular file", + "pipe", + "fifo", + "pseudo_terminal", + "dev_null" + }; + +enum test_stream_case + { + test_stream_stdin, + test_stream_stdout, + test_stream_stderr, + test_stream_fopen_r, + test_stream_fdopen_r, + test_stream_fopen_w, + test_stream_fdopen_w, + test_stream_count + }; + +static bool test_stream_reads[test_stream_count] = + { + true, + false, + false, + true, + true, + false, + false + }; + +static const char *const test_stream_name[test_stream_count] = + { + "stdin", + "stdout", + "stderr", + "fopen (read)", + "fdopen (read)", + "fopen (write)", + "fdopen (write)" + }; + +enum test_config_case + { + test_config_none, + test_config_unbuffered, + test_config_line, + test_config_fully, + test_config_count + }; + +static const char *const test_config_name[test_config_count] = + { + "no change", + "unbuffered", + "line buffered", + "fully buffered" + }; + +FILE *test_stream; + +char *test_file_name = NULL; +int pty_fd; +char *test_pipe_name = NULL; +int test_pipe[2]; + +/* This is either -1 or represents a pre-opened file descriptor for + the test as returned by prepare_test_file. */ +int test_fd; + +/*------------------------------------------------------------*/ + +/* Note that throughout this test we reopen, remove, and change + to/from a fifo, the test file. This would normally cause a race + condition, except that we're in a test container. No other process + can run in the test container simultaneously. */ + +void +prepare_test_data (void) +{ + buffer = (char *) xmalloc (bufsize); + +#ifndef INDEPENDENT_PART + /* Both file and pipe need this. */ + if (test_file_name == NULL) + { + debug; + int fd = create_temp_file ("tst-setvbuf2", &test_file_name); + TEST_VERIFY_EXIT (fd != -1); + struct stat64 st; + xfstat64 (fd, &st); + if (st.st_blksize > 0 && st.st_blksize < BUFSIZ) + blksize = st.st_blksize; + xclose (fd); + } +#endif + + for (size_t i = 0; i < 2 * BUFSIZ; i++) + { + unsigned char c = TEST_STRING[i % strlen (TEST_STRING)]; + test_data[i] = c; + } +} + +#ifndef INDEPENDENT_PART + +/* These functions provide a source/sink for the "other" side of any + pipe-style descriptor we're using for test. */ + +static pthread_t writer_thread_tid = 0; +static pthread_t reader_thread_tid = 0; + +typedef struct { + int fd; + const char *fname; +} ThreadData; +/* It's OK if this is static, we only run one at a time. */ +ThreadData thread_data; + +static void * +writer_thread_proc (void *closure) +{ + ThreadData *td = (ThreadData *) closure; + int fd; + int i; + ssize_t wn; + debug; + + if (td->fname) + td->fd = xopen (td->fname, O_WRONLY, 0777); + fd = td->fd; + + while (1) + { + i = 0; + while (i < BUFSIZ) + { + wn = write (fd, test_data + i, BUFSIZ - i); + if (wn <= 0) + break; + i += wn; + } + } + return NULL; +} + +static void * +reader_thread_proc (void *closure) +{ + ThreadData *td = (ThreadData *) closure; + int fd; + ssize_t rn; + int n = 0; + debug; + + if (td->fname) + td->fd = xopen (td->fname, O_RDONLY, 0777); + fd = td->fd; + + while (1) + { + char buf[BUFSIZ]; + rn = read (fd, buf, BUFSIZ); + if (rn <= 0) + break; + TEST_COMPARE_BLOB (buf, rn, test_data+n, rn); + n += rn; + } + return NULL; +} + +static void +start_writer_thread (int fd) +{ + debug; + thread_data.fd = fd; + thread_data.fname = NULL; + writer_thread_tid = xpthread_create (NULL, writer_thread_proc, + (void *)&thread_data); +} + +static void +start_writer_thread_n (const char *fname) +{ + debug; + thread_data.fd = 0; + thread_data.fname = fname; + writer_thread_tid = xpthread_create (NULL, writer_thread_proc, + (void *)&thread_data); +} + +static void +end_writer_thread (void) +{ + debug; + if (writer_thread_tid) + { + pthread_cancel (writer_thread_tid); + xpthread_join (writer_thread_tid); + xclose (thread_data.fd); + writer_thread_tid = 0; + } +} + +static void +start_reader_thread (int fd) +{ + debug; + thread_data.fd = fd; + thread_data.fname = NULL; + reader_thread_tid = xpthread_create (NULL, reader_thread_proc, + (void *)&thread_data); +} + +static void +start_reader_thread_n (const char *fname) +{ + debug; + thread_data.fd = 0; + thread_data.fname = fname; + reader_thread_tid = xpthread_create (NULL, reader_thread_proc, + (void *)&thread_data); +} + +static void +end_reader_thread (void) +{ + debug; + if (reader_thread_tid) + { + pthread_cancel (reader_thread_tid); + xpthread_join (reader_thread_tid); + xclose (thread_data.fd); + reader_thread_tid = 0; + } +} + +/*------------------------------------------------------------*/ + +/* These two functions are reponsible for choosing a file to be tested + against, typically by returning a filename but in a few cases also + providing a file descriptor (i.e. for fdopen). */ + +static const char * +prepare_test_file (enum test_source_case f, enum test_stream_case s) +{ + debug; + + test_fd = -1; + + switch (f) + { + case test_source_file: + { + if (test_stream_reads[f]) + { + debug; + FILE *fp = xfopen (test_file_name, "w"); + TEST_VERIFY_EXIT (fwrite (test_data, 1, 2 * BUFSIZ, fp) + == 2 * BUFSIZ); + xfclose (fp); + } + debug; + return test_file_name; + } + + case test_source_pipe: + { + debug; + xpipe (test_pipe); + if (test_stream_reads[s]) + { + start_writer_thread (test_pipe[1]); + test_fd = test_pipe[0]; + } + else + { + start_reader_thread (test_pipe[0]); + test_fd = test_pipe[1]; + } + test_pipe_name = xasprintf ("/proc/self/fd/%d", test_fd); + debug; + return test_pipe_name; + } + + case test_source_fifo: + { + /* We do not want to fail/exit if the file doesn't exist. */ + unlink (test_file_name); + xmkfifo (test_file_name, 0600); + debug; + if (test_stream_reads[s]) + start_writer_thread_n (test_file_name); + else + start_reader_thread_n (test_file_name); + debug; + return test_file_name; + } + + case test_source_pseudo_terminal: + { + support_openpty (&pty_fd, &test_fd, &test_pipe_name, NULL, NULL); + + debug; + if (test_stream_reads[s]) + start_writer_thread (pty_fd); + else + start_reader_thread (pty_fd); + + debug; + return test_pipe_name; + } + + case test_source_dev_null: + debug; + return "/dev/null"; + + default: + abort (); + } +} + +static void +unprepare_test_file (FILE *fp, + enum test_source_case f, + enum test_stream_case s) +{ + debug; + switch (f) + { + case test_source_file: + break; + + case test_source_pipe: + free (test_pipe_name); + if (test_stream_reads[s]) + end_writer_thread (); + else + end_reader_thread (); + break; + + case test_source_fifo: + if (test_stream_reads[s]) + end_writer_thread (); + else + end_reader_thread (); + unlink (test_file_name); + break; + + case test_source_pseudo_terminal: + free (test_pipe_name); + if (test_stream_reads[s]) + end_writer_thread (); + else + end_reader_thread (); + break; + + case test_source_dev_null: + break; + + default: + abort (); + } + debug; +} + +/*------------------------------------------------------------*/ + +/* This function takes a filename and returns a file descriptor, + opened according to the method requested. */ + +static FILE * +open_test_stream (enum test_source_case f, enum test_stream_case s) +{ + int fd; + FILE *fp; + const char *fname; + + debug; + fname = prepare_test_file (f, s); + if (fname == NULL) + return NULL; + + switch (s) + { + case test_stream_stdin: + fp = xfopen (fname, "r"); + break; + + case test_stream_stdout: + fp = xfopen (fname, "w"); + break; + + case test_stream_stderr: + fp = xfopen (fname, "w"); + break; + + case test_stream_fopen_r: + fp = xfopen (fname, "r"); + break; + + case test_stream_fdopen_r: + if (test_fd == -1) + fd = xopen (fname, O_RDONLY, 0); + else + fd = test_fd; + fp = fdopen (fd, "r"); + break; + + case test_stream_fopen_w: + fp = xfopen (fname, "w"); + break; + + case test_stream_fdopen_w: + fd = xopen (fname, O_WRONLY|O_CREAT|O_TRUNC, 0777); + fp = fdopen (fd, "w"); + break; + + default: + abort (); + } + TEST_VERIFY_EXIT (fp != NULL); + + if (f == test_source_pseudo_terminal) + { + struct termios t; + /* We disable the NL to CR-LF conversion so that we can compare + data without having to remove the extra CRs. */ + if (tcgetattr (fileno (fp), &t) < 0) + FAIL_EXIT1 ("tcgetattr failed: %m"); + t.c_oflag &= ~ONLCR; + if (tcsetattr (fileno (fp), TCSANOW, &t) < 0) + FAIL_EXIT1 ("tcsetattr failed: %m"); + } + + debug; + printf ("source %s stream %s file %s fd %d\n", + test_source_name[f], + test_stream_name[s], fname, fileno (fp)); + return fp; +} + +#endif + +/*------------------------------------------------------------*/ + +/* These functions do the actual testing - setting various buffering + options and verifying that they buffer as expected. */ + +static void +test_put_string (FILE *fp, const char *s, int count) +{ + while (*s && count--) + { + fputc (*s++, fp); + TEST_VERIFY_EXIT (!ferror (fp)); + } +} + +int +verify_fully_buffered (FILE *fp, + enum test_source_case f, + enum test_stream_case s, + enum test_config_case c) +{ + debug; + if (test_stream_reads[s]) + { + char buf[10]; + dumpfp (fp); + size_t fc = fread (buf, 1, 10 - 1, fp); + dumpfp (fp); + + ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; + + TEST_VERIFY (fp->_IO_read_base != NULL); + if (f == test_source_dev_null) + { + TEST_VERIFY (fc == 0); + TEST_VERIFY (count == 0); + } + else if (f == test_source_pseudo_terminal) + { + TEST_VERIFY (fc == 9); + TEST_VERIFY (count == 3 || count == 10); + } + else + { + TEST_VERIFY (fc == 9); + TEST_VERIFY (count == 10); + } + + /* We already checked for the first character being 'a'. */ + if (count > 1) + { + TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); + TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); + } + } + else + { + dumpfp (fp); + test_put_string (fp, test_data + 1, 10 - 1); + dumpfp (fp); + TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 10); + TEST_COMPARE_BLOB (fp->_IO_write_base, 10, test_data, 10); + } + + TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), 0); + if (c != test_config_none) + TEST_COMPARE (__fbufsize (fp), bufsize); + return 0; +} + +int +verify_line_buffered (FILE *fp, + enum test_source_case f, + enum test_stream_case s, + enum test_config_case c) +{ + debug; + /* "line buffered" for inputs is not really defined; what you really + want here is to control the device providing input. For GLIBC a + line-buffered input is treated as fully buffered. */ + if (test_stream_reads[s]) + { + char buf[10]; + dumpfp (fp); + size_t fc = fread (buf, 1, 10 - 1, fp); + dumpfp (fp); + + ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; + + TEST_VERIFY (fp->_IO_read_base != NULL); + if (f == test_source_dev_null) + { + TEST_VERIFY (fc == 0); + TEST_VERIFY (count == 0); + } + else if (f == test_source_pseudo_terminal) + { + TEST_VERIFY (fc == 9); + TEST_VERIFY (count == 3 || count == 10); + } + else + { + TEST_VERIFY (fc == 9); + TEST_VERIFY (count == 10); + } + + /* We already checked for the first character being 'a'. */ + if (count > 1) + { + TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); + TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); + } + } + else + { + dumpfp (fp); + test_put_string (fp, test_data + 1, 10 - 1); + dumpfp (fp); + TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 3); + /* The first "abcdef\n" got flushed, leaving "abc". */ + TEST_COMPARE_BLOB (fp->_IO_write_base, 3, test_data + 7, 3); + } + + TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), _IO_LINE_BUF); + if (c != test_config_none) + TEST_COMPARE (__fbufsize (fp), bufsize); + return 0; +} + +int +verify_unbuffered (FILE *fp, + enum test_source_case f, + enum test_stream_case s, + enum test_config_case c) +{ + debug; + if (test_stream_reads[s]) + { + /* We've already read one byte. */ + dumpfp (fp); + TEST_VERIFY (fp->_IO_read_base != NULL); + if (f == test_source_dev_null) + TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 0); + else + { + TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 1); + TEST_COMPARE (fp->_IO_read_base[0], test_data[0]); + TEST_VERIFY (fp->_IO_read_ptr == fp->_IO_read_end); + } + } + else + { + dumpfp (fp); + fputc (test_data[1], fp); + dumpfp (fp); + TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 0); + TEST_COMPARE (fp->_IO_write_base[0], test_data[1]); + TEST_VERIFY (fp->_IO_write_end == fp->_IO_write_base); + } + TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), + _IO_UNBUFFERED); + TEST_COMPARE (__fbufsize (fp), 1); + return 0; +} + +static int +do_setvbuf (FILE *fp, void *buf, int flags, int size, + enum test_stream_case s) +{ + if (s != test_stream_stdout) + printf ("SETVBUF %p %p %s %d\n", + fp, buf, + flags == _IONBF ? "_IONBF" + : flags == _IOLBF ? "_IOLBF" + : flags == _IOFBF ? "_IOFBF" + : "???", size); + if (setvbuf (fp, buf, flags, size)) + { + perror ("setvbuf"); + return 1; + } + return 0; +} + +int +do_second_part (FILE *fp, + enum test_source_case f, + enum test_stream_case s, + enum test_config_case c) +{ + /* At this point, FP is the stream to test according to the other + parameters. */ + + int rv = 0; + int flags_before; + int flags_after; + + debug; + + flags_before = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); + + /* This is where we do the thing we're testing for. */ + switch (c) + { + case test_config_none: + /* Buffering is unchanged. */ + break; + + case test_config_unbuffered: + do_setvbuf (fp, NULL, _IONBF, 0, s); + break; + + case test_config_line: + do_setvbuf (fp, buffer, _IOLBF, bufsize, s); + break; + + case test_config_fully: + do_setvbuf (fp, buffer, _IOFBF, bufsize, s); + break; + + default: + abort (); + } + + flags_after = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); + + /* Check the buffer mode after we touch it, if we touched it. */ + switch (c) + { + case test_config_none: + /* Buffering is unchanged, but may change on the first read/write. */ + TEST_COMPARE (flags_after, flags_before); + break; + + case test_config_unbuffered: + TEST_COMPARE (flags_after, _IO_UNBUFFERED); + break; + + case test_config_line: + TEST_COMPARE (flags_after, _IO_LINE_BUF); + break; + + case test_config_fully: + TEST_COMPARE (flags_after, 0); + break; + + default: + abort (); + } + + /* Glibc defers calculating the appropriate buffering mechanism + until it reads from or writes to the device. So we read one + character here, and account for that in the tests. */ + if (test_stream_reads[s]) + { + dumpfp (fp); + int c = fgetc (fp); + if (c != TEST_STRING[0] && f != test_source_dev_null) + FAIL ("first char read is %c not %c", c, TEST_STRING[0]); + dumpfp (fp); + } + else + { + dumpfp (fp); + fputc (TEST_STRING[0], fp); + dumpfp (fp); + } + + switch (fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)) + { + case _IO_LINE_BUF: + rv += verify_line_buffered (fp, f, s, c); + break; + + case _IO_UNBUFFERED: + rv += verify_unbuffered (fp, f, s, c); + break; + + case 0: /* Fully buffered. */ + rv += verify_fully_buffered (fp, f, s, c); + break; + + default: + abort (); + } + + + fclose (fp); + return rv; +} + +/*------------------------------------------------------------*/ + +#ifdef INDEPENDENT_PART +/* This part is the independent sub-process we call to test stdin et + al. */ + +int +main (int argc, char **argv) +{ + /* This is one of the subprocesses we created to test stdin et + al. */ + FILE *fp; + + /* If we're called as a regular test, instead of as a sub-process, + don't complain. */ + if (argc == 1) + return 0; + + if (argc != 4) + { + int i; + for (i = 0; i <= argc; i ++) + printf ("argv[%d] = `%s'\n", i, argv[i] ?: "(null)"); + FAIL_EXIT1 ("sub-process called wrong"); + } + + prepare_test_data (); + + enum test_source_case f = atoi (argv[1]); + enum test_stream_case s = atoi (argv[2]); + enum test_config_case c = atoi (argv[3]); + + if (s != test_stream_stdout) + printf ("\n\033[41mRunning test %s : %s : %s\033[0m\n", + test_source_name[f], + test_stream_name[s], + test_config_name[c]); + + switch (s) + { + case test_stream_stdin: + fp = stdin; + break; + case test_stream_stdout: + fp = stdout; + break; + case test_stream_stderr: + fp = stderr; + break; + default: + abort (); + } + + return do_second_part (fp, f, s, c); +} + +#else +/* This part is the standard test process. */ + +/* Spawn an independent sub-process with std* redirected. */ +int +recurse (FILE *fp, + enum test_source_case f, + enum test_stream_case s, + enum test_config_case c) +{ + /* We need to test stdin, stdout, or stderr, which means creating a + subprocess with one of those redirected from FP. */ + debug; + + pid_t pid; + int status; + + pid = fork (); + + switch (pid) + { + case -1: /* error */ + perror ("fork"); + return 1; + break; + + default: /* parent */ + fclose (fp); + xwaitpid (pid, &status, 0); + if (WIFEXITED (status) + && WEXITSTATUS (status) == 0) + return 0; + return 1; + + case 0: /* child */ + switch (s) + { + case test_stream_stdin: + xclose (0); + dup2 (fileno (fp), 0); + break; + case test_stream_stdout: + xclose (1); + dup2 (fileno (fp), 1); + break; + case test_stream_stderr: + xclose (2); + dup2 (fileno (fp), 2); + break; + default: + abort (); + } + fclose (fp); + + /* At this point, we have to run a program... which is tricky to + properly support for remote targets or crosses, because of + glibc versions etc. Hence we run in a test-container. */ + + char fs[10], ss[10], cs[10]; + sprintf (fs, "%d", f); + sprintf (ss, "%d", s); + sprintf (cs, "%d", c); + execl (IND_PROC, IND_PROC, fs, ss, cs, NULL); + if (s == test_stream_stdout) + fprintf (stderr, "execl (%s) failed, ", IND_PROC); + else + printf ("execl (%s) failed, ", IND_PROC); + perror ("The error was"); + exit (1); + break; + } + + return 0; +} + +int +do_test (void) +{ + int rv = 0; + + signal (SIGPIPE, SIG_IGN); + + prepare_test_data (); + + for (enum test_source_case f = 0; f < test_source_count; ++f) + for (enum test_stream_case s = 0; s < test_stream_count; ++s) + for (enum test_config_case c = 0; c < test_config_count; ++c) + { + printf ("\n\033[43mRunning test %s : %s : %s\033[0m\n", + test_source_name[f], + test_stream_name[s], + test_config_name[c]); + + FILE *fp = open_test_stream (f, s); + + if (fp) + { + + if (s <= test_stream_stderr) + rv += recurse (fp, f, s, c); + else + rv += do_second_part (fp, f, s, c); + + unprepare_test_file (fp, f, s); + } + } + + free (buffer); + + printf ("return %d\n", rv); + return rv; +} + +# include +#endif +