Util: Start cleaning up some bounded string copies

This commit is contained in:
Vicki Pfau 2020-07-06 16:08:23 -07:00
parent 3be21bf595
commit 2f066a9790
6 changed files with 86 additions and 57 deletions

View File

@ -281,6 +281,7 @@ include(CheckFunctionExists)
include(CheckIncludeFiles) include(CheckIncludeFiles)
check_function_exists(strdup HAVE_STRDUP) check_function_exists(strdup HAVE_STRDUP)
check_function_exists(strndup HAVE_STRNDUP) check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(strlcpy HAVE_STRLCPY)
if(NOT DEFINED PSP2) if(NOT DEFINED PSP2)
check_function_exists(localtime_r HAVE_LOCALTIME_R) check_function_exists(localtime_r HAVE_LOCALTIME_R)
endif() endif()
@ -359,6 +360,10 @@ if(HAVE_STRNDUP)
list(APPEND FUNCTION_DEFINES HAVE_STRNDUP) list(APPEND FUNCTION_DEFINES HAVE_STRNDUP)
endif() endif()
if(HAVE_STRLCPY)
list(APPEND FUNCTION_DEFINES HAVE_STRLCPY)
endif()
if(HAVE_LOCALTIME_R) if(HAVE_LOCALTIME_R)
list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R) list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R)
endif() endif()

View File

@ -19,6 +19,10 @@ char* strndup(const char* start, size_t len);
char* strdup(const char* str); char* strdup(const char* str);
#endif #endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize);
#endif
char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len); char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len);
bool endswith(const char* restrict s1, const char* restrict end); bool endswith(const char* restrict s1, const char* restrict end);
bool startswith(const char* restrict s1, const char* restrict start); bool startswith(const char* restrict s1, const char* restrict start);

View File

@ -6,6 +6,7 @@
#include <mgba/internal/arm/decoder.h> #include <mgba/internal/arm/decoder.h>
#include <mgba/internal/arm/decoder-inlines.h> #include <mgba/internal/arm/decoder-inlines.h>
#include <mgba-util/string.h>
#define ADVANCE(AMOUNT) \ #define ADVANCE(AMOUNT) \
if (AMOUNT >= blen) { \ if (AMOUNT >= blen) { \
@ -45,22 +46,22 @@ static const char* _armConditions[] = {
static int _decodeRegister(int reg, char* buffer, int blen) { static int _decodeRegister(int reg, char* buffer, int blen) {
switch (reg) { switch (reg) {
case ARM_SP: case ARM_SP:
strncpy(buffer, "sp", blen - 1); strlcpy(buffer, "sp", blen);
return 2; return 2;
case ARM_LR: case ARM_LR:
strncpy(buffer, "lr", blen - 1); strlcpy(buffer, "lr", blen);
return 2; return 2;
case ARM_PC: case ARM_PC:
strncpy(buffer, "pc", blen - 1); strlcpy(buffer, "pc", blen);
return 2; return 2;
case ARM_CPSR: case ARM_CPSR:
strncpy(buffer, "cpsr", blen - 1); strlcpy(buffer, "cpsr", blen);
return 4; return 4;
case ARM_SPSR: case ARM_SPSR:
strncpy(buffer, "spsr", blen - 1); strlcpy(buffer, "spsr", blen);
return 4; return 4;
default: default:
return snprintf(buffer, blen - 1, "r%i", reg); return snprintf(buffer, blen, "r%i", reg);
} }
} }
@ -69,7 +70,7 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
return 0; return 0;
} }
int total = 0; int total = 0;
strncpy(buffer, "{", blen - 1); strlcpy(buffer, "{", blen);
ADVANCE(1); ADVANCE(1);
int i; int i;
int start = -1; int start = -1;
@ -86,12 +87,12 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
if (end > start) { if (end > start) {
written = _decodeRegister(start, buffer, blen); written = _decodeRegister(start, buffer, blen);
ADVANCE(written); ADVANCE(written);
strncpy(buffer, "-", blen - 1); strlcpy(buffer, "-", blen);
ADVANCE(1); ADVANCE(1);
} }
written = _decodeRegister(end, buffer, blen); written = _decodeRegister(end, buffer, blen);
ADVANCE(written); ADVANCE(written);
strncpy(buffer, ",", blen - 1); strlcpy(buffer, ",", blen);
ADVANCE(1); ADVANCE(1);
start = i; start = i;
end = i; end = i;
@ -103,13 +104,13 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
if (end > start) { if (end > start) {
written = _decodeRegister(start, buffer, blen); written = _decodeRegister(start, buffer, blen);
ADVANCE(written); ADVANCE(written);
strncpy(buffer, "-", blen - 1); strlcpy(buffer, "-", blen);
ADVANCE(1); ADVANCE(1);
} }
written = _decodeRegister(end, buffer, blen); written = _decodeRegister(end, buffer, blen);
ADVANCE(written); ADVANCE(written);
} }
strncpy(buffer, "}", blen - 1); strlcpy(buffer, "}", blen);
ADVANCE(1); ADVANCE(1);
return total; return total;
} }
@ -119,29 +120,29 @@ static int _decodePSR(int psrBits, char* buffer, int blen) {
return 0; return 0;
} }
int total = 0; int total = 0;
strncpy(buffer, "_", blen - 1); strlcpy(buffer, "_", blen);
ADVANCE(1); ADVANCE(1);
if (psrBits & ARM_PSR_C) { if (psrBits & ARM_PSR_C) {
strncpy(buffer, "c", blen - 1); strlcpy(buffer, "c", blen);
ADVANCE(1); ADVANCE(1);
} }
if (psrBits & ARM_PSR_X) { if (psrBits & ARM_PSR_X) {
strncpy(buffer, "x", blen - 1); strlcpy(buffer, "x", blen);
ADVANCE(1); ADVANCE(1);
} }
if (psrBits & ARM_PSR_S) { if (psrBits & ARM_PSR_S) {
strncpy(buffer, "s", blen - 1); strlcpy(buffer, "s", blen);
ADVANCE(1); ADVANCE(1);
} }
if (psrBits & ARM_PSR_F) { if (psrBits & ARM_PSR_F) {
strncpy(buffer, "f", blen - 1); strlcpy(buffer, "f", blen);
ADVANCE(1); ADVANCE(1);
} }
return total; return total;
} }
static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) { static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) {
return snprintf(buffer, blen - 1, "$%08X", address + pc); return snprintf(buffer, blen, "$%08X", address + pc);
} }
static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) { static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) {
@ -149,7 +150,7 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
return 0; return 0;
} }
int total = 0; int total = 0;
strncpy(buffer, "[", blen - 1); strlcpy(buffer, "[", blen);
ADVANCE(1); ADVANCE(1);
int written; int written;
if (memory.format & ARM_MEMORY_REGISTER_BASE) { if (memory.format & ARM_MEMORY_REGISTER_BASE) {
@ -160,26 +161,26 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
written = _decodeRegister(memory.baseReg, buffer, blen); written = _decodeRegister(memory.baseReg, buffer, blen);
ADVANCE(written); ADVANCE(written);
if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) { if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) {
strncpy(buffer, ", ", blen - 1); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
} }
} }
} }
if (memory.format & ARM_MEMORY_POST_INCREMENT) { if (memory.format & ARM_MEMORY_POST_INCREMENT) {
strncpy(buffer, "], ", blen - 1); strlcpy(buffer, "], ", blen);
ADVANCE(3); ADVANCE(3);
} }
if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) { if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) {
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) { if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
written = snprintf(buffer, blen - 1, "#-%i", memory.offset.immediate); written = snprintf(buffer, blen, "#-%i", memory.offset.immediate);
ADVANCE(written); ADVANCE(written);
} else { } else {
written = snprintf(buffer, blen - 1, "#%i", memory.offset.immediate); written = snprintf(buffer, blen, "#%i", memory.offset.immediate);
ADVANCE(written); ADVANCE(written);
} }
} else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) { } else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) {
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) { if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
strncpy(buffer, "-", blen - 1); strlcpy(buffer, "-", blen);
ADVANCE(1); ADVANCE(1);
} }
written = _decodeRegister(memory.offset.reg, buffer, blen); written = _decodeRegister(memory.offset.reg, buffer, blen);
@ -191,11 +192,11 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
} }
if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) { if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) {
strncpy(buffer, "]", blen - 1); strlcpy(buffer, "]", blen);
ADVANCE(1); ADVANCE(1);
} }
if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) { if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) {
strncpy(buffer, "!", blen - 1); strlcpy(buffer, "!", blen);
ADVANCE(1); ADVANCE(1);
} }
return total; return total;
@ -206,33 +207,33 @@ static int _decodeShift(union ARMOperand op, bool reg, char* buffer, int blen) {
return 0; return 0;
} }
int total = 0; int total = 0;
strncpy(buffer, ", ", blen - 1); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
int written; int written;
switch (op.shifterOp) { switch (op.shifterOp) {
case ARM_SHIFT_LSL: case ARM_SHIFT_LSL:
strncpy(buffer, "lsl ", blen - 1); strlcpy(buffer, "lsl ", blen);
ADVANCE(4); ADVANCE(4);
break; break;
case ARM_SHIFT_LSR: case ARM_SHIFT_LSR:
strncpy(buffer, "lsr ", blen - 1); strlcpy(buffer, "lsr ", blen);
ADVANCE(4); ADVANCE(4);
break; break;
case ARM_SHIFT_ASR: case ARM_SHIFT_ASR:
strncpy(buffer, "asr ", blen - 1); strlcpy(buffer, "asr ", blen);
ADVANCE(4); ADVANCE(4);
break; break;
case ARM_SHIFT_ROR: case ARM_SHIFT_ROR:
strncpy(buffer, "ror ", blen - 1); strlcpy(buffer, "ror ", blen);
ADVANCE(4); ADVANCE(4);
break; break;
case ARM_SHIFT_RRX: case ARM_SHIFT_RRX:
strncpy(buffer, "rrx", blen - 1); strlcpy(buffer, "rrx", blen);
ADVANCE(3); ADVANCE(3);
return total; return total;
} }
if (!reg) { if (!reg) {
written = snprintf(buffer, blen - 1, "#%i", op.shifterImm); written = snprintf(buffer, blen, "#%i", op.shifterImm);
} else { } else {
written = _decodeRegister(op.shifterReg, buffer, blen); written = _decodeRegister(op.shifterReg, buffer, blen);
} }
@ -369,7 +370,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
default: default:
break; break;
} }
written = snprintf(buffer, blen - 1, "%s%s%s ", mnemonic, cond, flags); written = snprintf(buffer, blen, "%s%s%s ", mnemonic, cond, flags);
ADVANCE(written); ADVANCE(written);
switch (info->mnemonic) { switch (info->mnemonic) {
@ -378,15 +379,15 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
written = _decodeRegister(info->memory.baseReg, buffer, blen); written = _decodeRegister(info->memory.baseReg, buffer, blen);
ADVANCE(written); ADVANCE(written);
if (info->memory.format & ARM_MEMORY_WRITEBACK) { if (info->memory.format & ARM_MEMORY_WRITEBACK) {
strncpy(buffer, "!", blen - 1); strlcpy(buffer, "!", blen);
ADVANCE(1); ADVANCE(1);
} }
strncpy(buffer, ", ", blen - 1); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
written = _decodeRegisterList(info->op1.immediate, buffer, blen); written = _decodeRegisterList(info->op1.immediate, buffer, blen);
ADVANCE(written); ADVANCE(written);
if (info->memory.format & ARM_MEMORY_SPSR_SWAP) { if (info->memory.format & ARM_MEMORY_SPSR_SWAP) {
strncpy(buffer, "^", blen - 1); strlcpy(buffer, "^", blen);
ADVANCE(1); ADVANCE(1);
} }
break; break;
@ -399,7 +400,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
break; break;
default: default:
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) { if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
written = snprintf(buffer, blen - 1, "#%i", info->op1.immediate); written = snprintf(buffer, blen, "#%i", info->op1.immediate);
ADVANCE(written); ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_1) { } else if (info->operandFormat & ARM_OPERAND_MEMORY_1) {
written = _decodeMemory(info->memory, pc, buffer, blen); written = _decodeMemory(info->memory, pc, buffer, blen);
@ -420,11 +421,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written); ADVANCE(written);
} }
if (info->operandFormat & ARM_OPERAND_2) { if (info->operandFormat & ARM_OPERAND_2) {
strncpy(buffer, ", ", blen); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
} }
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) { if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) {
written = snprintf(buffer, blen - 1, "#%i", info->op2.immediate); written = snprintf(buffer, blen, "#%i", info->op2.immediate);
ADVANCE(written); ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_2) { } else if (info->operandFormat & ARM_OPERAND_MEMORY_2) {
written = _decodeMemory(info->memory, pc, buffer, blen); written = _decodeMemory(info->memory, pc, buffer, blen);
@ -441,11 +442,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written); ADVANCE(written);
} }
if (info->operandFormat & ARM_OPERAND_3) { if (info->operandFormat & ARM_OPERAND_3) {
strncpy(buffer, ", ", blen - 1); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
} }
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) { if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) {
written = snprintf(buffer, blen - 1, "#%i", info->op3.immediate); written = snprintf(buffer, blen, "#%i", info->op3.immediate);
ADVANCE(written); ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_3) { } else if (info->operandFormat & ARM_OPERAND_MEMORY_3) {
written = _decodeMemory(info->memory, pc, buffer, blen); written = _decodeMemory(info->memory, pc, buffer, blen);
@ -462,11 +463,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written); ADVANCE(written);
} }
if (info->operandFormat & ARM_OPERAND_4) { if (info->operandFormat & ARM_OPERAND_4) {
strncpy(buffer, ", ", blen - 1); strlcpy(buffer, ", ", blen);
ADVANCE(2); ADVANCE(2);
} }
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) { if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) {
written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate); written = snprintf(buffer, blen, "#%i", info->op4.immediate);
ADVANCE(written); ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_4) { } else if (info->operandFormat & ARM_OPERAND_MEMORY_4) {
written = _decodeMemory(info->memory, pc, buffer, blen); written = _decodeMemory(info->memory, pc, buffer, blen);

View File

@ -7,6 +7,7 @@
#include <mgba/internal/sm83/emitter-sm83.h> #include <mgba/internal/sm83/emitter-sm83.h>
#include <mgba/internal/sm83/sm83.h> #include <mgba/internal/sm83/sm83.h>
#include <mgba-util/string.h>
typedef size_t (*SM83Decoder)(uint8_t opcode, struct SM83InstructionInfo* info); typedef size_t (*SM83Decoder)(uint8_t opcode, struct SM83InstructionInfo* info);
@ -504,39 +505,39 @@ static int _decodeOperand(struct SM83Operand op, uint16_t pc, char* buffer, int
return 0; return 0;
} }
strncpy(buffer, " ", blen - 1); strlcpy(buffer, " ", blen);
ADVANCE(1); ADVANCE(1);
if (op.flags & SM83_OP_FLAG_MEMORY) { if (op.flags & SM83_OP_FLAG_MEMORY) {
strncpy(buffer, "[", blen - 1); strlcpy(buffer, "[", blen);
ADVANCE(1); ADVANCE(1);
} }
if (op.reg) { if (op.reg) {
int written = snprintf(buffer, blen - 1, "%s", _sm83Registers[op.reg]); int written = snprintf(buffer, blen, "%s", _sm83Registers[op.reg]);
ADVANCE(written); ADVANCE(written);
} else { } else {
int written; int written;
if (op.flags & SM83_OP_FLAG_RELATIVE) { if (op.flags & SM83_OP_FLAG_RELATIVE) {
written = snprintf(buffer, blen - 1, "$%04X", pc + (int8_t) op.immediate); written = snprintf(buffer, blen, "$%04X", pc + (int8_t) op.immediate);
} else { } else {
written = snprintf(buffer, blen - 1, "$%02X", op.immediate); written = snprintf(buffer, blen, "$%02X", op.immediate);
} }
ADVANCE(written); ADVANCE(written);
if (op.reg) { if (op.reg) {
strncpy(buffer, "+", blen - 1); strlcpy(buffer, "+", blen);
ADVANCE(1); ADVANCE(1);
} }
} }
if (op.flags & SM83_OP_FLAG_INCREMENT) { if (op.flags & SM83_OP_FLAG_INCREMENT) {
strncpy(buffer, "+", blen - 1); strlcpy(buffer, "+", blen);
ADVANCE(1); ADVANCE(1);
} }
if (op.flags & SM83_OP_FLAG_DECREMENT) { if (op.flags & SM83_OP_FLAG_DECREMENT) {
strncpy(buffer, "-", blen - 1); strlcpy(buffer, "-", blen);
ADVANCE(1); ADVANCE(1);
} }
if (op.flags & SM83_OP_FLAG_MEMORY) { if (op.flags & SM83_OP_FLAG_MEMORY) {
strncpy(buffer, "]", blen - 1); strlcpy(buffer, "]", blen);
ADVANCE(1); ADVANCE(1);
} }
return total; return total;
@ -548,15 +549,15 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
int total = 0; int total = 0;
const char* cond = _sm83Conditions[info->condition]; const char* cond = _sm83Conditions[info->condition];
written = snprintf(buffer, blen - 1, "%s", mnemonic); written = snprintf(buffer, blen, "%s", mnemonic);
ADVANCE(written); ADVANCE(written);
if (cond) { if (cond) {
written = snprintf(buffer, blen - 1, " %s", cond); written = snprintf(buffer, blen, " %s", cond);
ADVANCE(written); ADVANCE(written);
if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) { if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) {
strncpy(buffer, ",", blen - 1); strlcpy(buffer, ",", blen);
ADVANCE(1); ADVANCE(1);
} }
} }
@ -568,7 +569,7 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) { if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) {
if (written) { if (written) {
strncpy(buffer, ",", blen - 1); strlcpy(buffer, ",", blen);
ADVANCE(1); ADVANCE(1);
} }
written = _decodeOperand(info->op2, pc, buffer, blen); written = _decodeOperand(info->op2, pc, buffer, blen);

View File

@ -7,6 +7,7 @@
#include <mgba-util/gui/font.h> #include <mgba-util/gui/font.h>
#include <mgba-util/gui/menu.h> #include <mgba-util/gui/menu.h>
#include <mgba-util/string.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
#include <stdlib.h> #include <stdlib.h>
@ -200,7 +201,7 @@ bool GUISelectFile(struct GUIParams* params, char* outPath, size_t outLen, bool
_cleanFiles(&menu.items); _cleanFiles(&menu.items);
GUIMenuItemListDeinit(&menu.items); GUIMenuItemListDeinit(&menu.items);
menu.items = newFiles; menu.items = newFiles;
strncpy(params->currentPath, outPath, PATH_MAX); strlcpy(params->currentPath, outPath, PATH_MAX);
} }
} }
params->fileIndex = 0; params->fileIndex = 0;

View File

@ -31,6 +31,23 @@ char* strdup(const char* str) {
} }
#endif #endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize) {
size_t i = 0;
for (; src[i] && dstsize > 1; ++i) {
dst[i] = src[i];
--dstsize;
}
if (dstsize) {
dst[i] = '\0';
}
while (src[i]) {
++i;
}
return i;
}
#endif
char* strnrstr(const char* restrict haystack, const char* restrict needle, size_t len) { char* strnrstr(const char* restrict haystack, const char* restrict needle, size_t len) {
char* last = 0; char* last = 0;
const char* next = haystack; const char* next = haystack;