1 /*- 2 * Copyright (c) 2023 The FreeBSD Foundation 3 * 4 * This software was developed by Robert Clausecker <fuz@FreeBSD.org> 5 * under sponsorship from the FreeBSD Foundation. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE 27 */ 28 29 30 #include <atf-c.h> 31 #include <assert.h> 32 #include <limits.h> 33 #include <stdbool.h> 34 #include <stddef.h> 35 #include <string.h> 36 37 enum { 38 MAXALIGN = 16, /* test all offsets from this alignment */ 39 MAXBUF = 64, /* test up to this buffer length */ 40 }; 41 42 enum { NOMATCH, MATCH }; 43 44 #ifdef STRSPN 45 #define STRXSPN strspn 46 #else 47 #define STRXSPN strcspn 48 #endif 49 50 static void 51 testcase(char *buf, size_t buflen, char *set, size_t setlen, int want_match) 52 { 53 size_t i, outcome, expected; 54 55 assert(setlen < UCHAR_MAX - 2); 56 57 for (i = 0; i < buflen; i++) 58 #ifdef STRSPN 59 buf[i] = UCHAR_MAX - i % (setlen > 0 ? setlen : 1); 60 #else /* strcspn */ 61 buf[i] = 1 + i % (UCHAR_MAX - setlen - 1); 62 #endif 63 64 buf[i] = '\0'; 65 66 for (i = 0; i < setlen; i++) 67 set[i] = UCHAR_MAX - i; 68 69 set[i] = '\0'; 70 71 #ifdef STRSPN 72 if (setlen == 0) 73 expected = 0; 74 else if (want_match == MATCH && buflen > 0) { 75 buf[buflen - 1] = 1; 76 expected = buflen - 1; 77 } else 78 expected = buflen; 79 #else /* strcspn */ 80 if (want_match == MATCH && buflen > 0 && setlen > 0) { 81 buf[buflen - 1] = UCHAR_MAX; 82 expected = buflen - 1; 83 } else 84 expected = buflen; 85 #endif 86 87 outcome = STRXSPN(buf, set); 88 ATF_CHECK_EQ_MSG(expected, outcome, "%s(%p[%zu], %p[%zu]) = %zu != %zu", 89 __XSTRING(STRXSPN), buf, buflen, set, setlen, outcome, expected); 90 } 91 92 /* test set with all alignments and lengths of buf */ 93 static void 94 test_buf_alignments(char *set, size_t setlen, int want_match) 95 { 96 char buf[MAXALIGN + MAXBUF + 1]; 97 size_t i, j; 98 99 for (i = 0; i < MAXALIGN; i++) 100 for (j = 0; j <= MAXBUF; j++) 101 testcase(buf + i, j, set, setlen, want_match); 102 } 103 104 /* test buf with all alignments and lengths of set */ 105 static void 106 test_set_alignments(char *buf, size_t buflen, int want_match) 107 { 108 char set[MAXALIGN + MAXBUF + 1]; 109 size_t i, j; 110 111 for (i = 0; i < MAXALIGN; i++) 112 for (j = 0; j <= MAXBUF; j++) 113 testcase(buf, buflen, set + i, j, want_match); 114 } 115 116 ATF_TC_WITHOUT_HEAD(buf_alignments); 117 ATF_TC_BODY(buf_alignments, tc) 118 { 119 char set[41]; 120 121 test_buf_alignments(set, 0, MATCH); 122 test_buf_alignments(set, 1, MATCH); 123 test_buf_alignments(set, 5, MATCH); 124 test_buf_alignments(set, 20, MATCH); 125 test_buf_alignments(set, 40, MATCH); 126 127 test_buf_alignments(set, 0, NOMATCH); 128 test_buf_alignments(set, 1, NOMATCH); 129 test_buf_alignments(set, 5, NOMATCH); 130 test_buf_alignments(set, 20, NOMATCH); 131 test_buf_alignments(set, 40, NOMATCH); 132 } 133 134 ATF_TC_WITHOUT_HEAD(set_alignments); 135 ATF_TC_BODY(set_alignments, tc) 136 { 137 char buf[31]; 138 139 test_set_alignments(buf, 0, MATCH); 140 test_set_alignments(buf, 10, MATCH); 141 test_set_alignments(buf, 20, MATCH); 142 test_set_alignments(buf, 30, MATCH); 143 144 test_set_alignments(buf, 0, NOMATCH); 145 test_set_alignments(buf, 10, NOMATCH); 146 test_set_alignments(buf, 20, NOMATCH); 147 test_set_alignments(buf, 30, NOMATCH); 148 } 149 150 #ifndef STRSPN 151 /* test all positions in which set could match buf */ 152 static void 153 test_match_positions(char *buf, char *set, size_t buflen, size_t setlen) 154 { 155 size_t i, j, outcome; 156 157 memset(buf, '-', buflen); 158 159 for (i = 0; i < setlen; i++) 160 set[i] = 'A' + i; 161 162 buf[buflen] = '\0'; 163 set[setlen] = '\0'; 164 165 /* 166 * Check for (mis)match at buffer position i 167 * against set position j. 168 */ 169 for (i = 0; i < buflen; i++) { 170 for (j = 0; j < setlen; j++) { 171 buf[i] = set[j]; 172 173 outcome = strcspn(buf, set); 174 ATF_CHECK_EQ_MSG(i, outcome, 175 "strcspn(\"%s\", \"%s\") = %zu != %zu", 176 buf, set, outcome, i); 177 } 178 179 buf[i] = '-'; 180 } 181 } 182 183 ATF_TC_WITHOUT_HEAD(match_positions); 184 ATF_TC_BODY(match_positions, tc) 185 { 186 char buf[129], set[65]; 187 188 test_match_positions(buf, set, 128, 64); 189 test_match_positions(buf, set, 64, 64); 190 test_match_positions(buf, set, 32, 64); 191 test_match_positions(buf, set, 16, 64); 192 test_match_positions(buf, set, 8, 64); 193 test_match_positions(buf, set, 128, 32); 194 test_match_positions(buf, set, 64, 32); 195 test_match_positions(buf, set, 32, 32); 196 test_match_positions(buf, set, 16, 32); 197 test_match_positions(buf, set, 8, 32); 198 test_match_positions(buf, set, 128, 16); 199 test_match_positions(buf, set, 64, 16); 200 test_match_positions(buf, set, 32, 16); 201 test_match_positions(buf, set, 16, 16); 202 test_match_positions(buf, set, 8, 16); 203 test_match_positions(buf, set, 128, 8); 204 test_match_positions(buf, set, 64, 8); 205 test_match_positions(buf, set, 32, 8); 206 test_match_positions(buf, set, 16, 8); 207 test_match_positions(buf, set, 8, 8); 208 } 209 210 /* if there are two matches, check that the earlier match is taken */ 211 static void 212 test_match_order(char *buf, char *set, size_t buflen, size_t setlen) 213 { 214 size_t i, j, k, l, outcome; 215 216 memset(buf, '-', buflen); 217 218 for (i = 0; i < setlen; i++) 219 set[i] = 'A' + i; 220 221 buf[buflen] = '\0'; 222 set[setlen] = '\0'; 223 224 for (i = 0; i < setlen; i++) 225 for (j = 0; j < setlen; j++) 226 for (k = 0; k + 1 < buflen; k++) 227 for (l = k + 1; l < buflen; l++) { 228 buf[k] = set[i]; 229 buf[l] = set[j]; 230 outcome = strcspn(buf, set); 231 ATF_CHECK_EQ_MSG(k, outcome, 232 "strcspn(\"%s\", \"%s\") = %zu != %zu", 233 buf, set, outcome, k); 234 buf[k] = '-'; 235 buf[l] = '-'; 236 } 237 } 238 239 ATF_TC_WITHOUT_HEAD(match_order); 240 ATF_TC_BODY(match_order, tc) 241 { 242 char buf[33], set[65]; 243 244 test_match_order(buf, set, 32, 64); 245 test_match_order(buf, set, 16, 64); 246 test_match_order(buf, set, 8, 64); 247 test_match_order(buf, set, 32, 32); 248 test_match_order(buf, set, 16, 32); 249 test_match_order(buf, set, 8, 32); 250 test_match_order(buf, set, 32, 16); 251 test_match_order(buf, set, 16, 16); 252 test_match_order(buf, set, 8, 16); 253 test_match_order(buf, set, 32, 8); 254 test_match_order(buf, set, 16, 8); 255 test_match_order(buf, set, 8, 8); 256 } 257 #endif /* !defined(STRSPN) */ 258 259 ATF_TP_ADD_TCS(tp) 260 { 261 ATF_TP_ADD_TC(tp, buf_alignments); 262 ATF_TP_ADD_TC(tp, set_alignments); 263 #ifndef STRSPN 264 ATF_TP_ADD_TC(tp, match_positions); 265 ATF_TP_ADD_TC(tp, match_order); 266 #endif 267 268 return (atf_no_error()); 269 } 270