mirror of
https://github.com/cc65/cc65.git
synced 2025-01-13 09:31:53 +00:00
Merge pull request #2434 from colinleroy/strcasestr
Implement strcasestr
This commit is contained in:
commit
0541b65aa4
@ -780,6 +780,7 @@ communication, see also <tt>testcode/lib/ser-test.c</tt>.
|
||||
<item><ref id="strqtok" name="strqtok">
|
||||
<item><ref id="strrchr" name="strrchr">
|
||||
<item><ref id="strspn" name="strspn">
|
||||
<item><ref id="strcasestr" name="strcasestr">
|
||||
<item><ref id="strstr" name="strstr">
|
||||
<item><ref id="strtok" name="strtok">
|
||||
<item><ref id="strxfrm" name="strxfrm">
|
||||
@ -7899,22 +7900,47 @@ be used in presence of a prototype.
|
||||
</quote>
|
||||
|
||||
|
||||
<sect1>strstr<label id="strstr"><p>
|
||||
<sect1>strcasestr<label id="strcasestr"><p>
|
||||
|
||||
<quote>
|
||||
<descrip>
|
||||
<tag/Function/Find a substring.
|
||||
<tag/Function/Find a substring, case-insensitive.
|
||||
<tag/Header/<tt/<ref id="string.h" name="string.h">/
|
||||
<tag/Declaration/<tt/char* __fastcall__ strstr (const char* str, const char* substr);/
|
||||
<tag/Description/<tt/strstr/ searches for the first occurrence of the string
|
||||
<tt/substr/ within <tt/str/. If found, it returns a pointer to the copy,
|
||||
otherwise it returns <tt/NULL/.
|
||||
<tag/Declaration/<tt/char* __fastcall__ strcasestr (const char* str, const char* substr);/
|
||||
<tag/Description/<tt/strcasestr/ searches for the first occurrence of the string
|
||||
<tt/substr/ within <tt/str/. If found, it returns a pointer to the start of the
|
||||
match in <tt/str/, otherwise it returns <tt/NULL/.
|
||||
<tag/Notes/<itemize>
|
||||
<item>The function is only available as fastcall function, so it may only
|
||||
be used in presence of a prototype.
|
||||
</itemize>
|
||||
<tag/Availability/ISO 9899
|
||||
<tag/See also/
|
||||
<ref id="strstr" name="strstr">,
|
||||
<ref id="strcspn" name="strcspn">,
|
||||
<ref id="strspn" name="strspn">
|
||||
<tag/Example/None.
|
||||
</descrip>
|
||||
</quote>
|
||||
|
||||
|
||||
<sect1>strstr<label id="strstr"><p>
|
||||
|
||||
<quote>
|
||||
<descrip>
|
||||
<tag/Function/Find a substring, case-sensitive.
|
||||
<tag/Header/<tt/<ref id="string.h" name="string.h">/
|
||||
<tag/Declaration/<tt/char* __fastcall__ strstr (const char* str, const char* substr);/
|
||||
<tag/Description/<tt/strstr/ searches for the first occurrence of the string
|
||||
<tt/substr/ within <tt/str/. If found, it returns a pointer to the start of the
|
||||
match in <tt/str/, otherwise it returns <tt/NULL/.
|
||||
<tag/Notes/<itemize>
|
||||
<item>The function is only available as fastcall function, so it may only
|
||||
be used in presence of a prototype.
|
||||
</itemize>
|
||||
<tag/Availability/ISO 9899
|
||||
<tag/See also/
|
||||
<ref id="strcasestr" name="strcasestr">,
|
||||
<ref id="strcspn" name="strcspn">,
|
||||
<ref id="strspn" name="strspn">
|
||||
<tag/Example/None.
|
||||
|
@ -81,6 +81,7 @@ void __fastcall__ bzero (void* ptr, size_t n); /* BSD */
|
||||
char* __fastcall__ strdup (const char* s); /* SYSV/BSD */
|
||||
int __fastcall__ stricmp (const char* s1, const char* s2); /* DOS/Windows */
|
||||
int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */
|
||||
char* __fastcall__ strcasestr (const char* str, const char* substr);
|
||||
int __fastcall__ strnicmp (const char* s1, const char* s2, size_t count); /* DOS/Windows */
|
||||
int __fastcall__ strncasecmp (const char* s1, const char* s2, size_t count); /* Same for Unix */
|
||||
size_t __fastcall__ strnlen (const char* s, size_t maxlen); /* POSIX.1-2008 */
|
||||
|
95
libsrc/common/strcasestr.s
Normal file
95
libsrc/common/strcasestr.s
Normal file
@ -0,0 +1,95 @@
|
||||
;
|
||||
; Ullrich von Bassewitz, 11.12.1998
|
||||
;
|
||||
; char* strcasestr (const char* haystack, const char* needle);
|
||||
;
|
||||
|
||||
.export _strcasestr
|
||||
.import popptr1, return0, tolowerdirect
|
||||
.importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4
|
||||
.include "ctype.inc"
|
||||
|
||||
.segment "LOWCODE"
|
||||
|
||||
_strcasestr:
|
||||
sta ptr2 ; Save needle
|
||||
stx ptr2+1
|
||||
sta ptr4 ; Setup temp copy for later
|
||||
|
||||
jsr popptr1 ; Get haystack to ptr1
|
||||
|
||||
; If needle is empty, return haystack
|
||||
|
||||
; ldy #$00 Y=0 guaranteed by popptr1
|
||||
lda (ptr2),y ; Get first byte of needle
|
||||
beq @Found ; Needle is empty --> we're done
|
||||
|
||||
; Search for the beginning of the string (this is not an optimal search
|
||||
; strategy [in fact, it's pretty dumb], but it's simple to implement).
|
||||
|
||||
jsr tolowerdirect ; Lowercase
|
||||
sta tmp1 ; Save start of needle
|
||||
@L1: lda (ptr1),y ; Get next char from haystack
|
||||
beq @NotFound ; Jump if end
|
||||
|
||||
jsr tolowerdirect ; Lowercase
|
||||
cmp tmp1 ; Start of needle found?
|
||||
beq @L2 ; Jump if so
|
||||
iny ; Next char
|
||||
bne @L1
|
||||
inc ptr1+1 ; Bump high byte
|
||||
bne @L1 ; Branch always
|
||||
|
||||
; We found the start of needle in haystack
|
||||
|
||||
@L2: tya ; Get offset
|
||||
clc
|
||||
adc ptr1
|
||||
sta ptr1 ; Make ptr1 point to start
|
||||
bcc @L3
|
||||
inc ptr1+1
|
||||
|
||||
; ptr1 points to the start of needle in haystack now. Setup temporary pointers for the
|
||||
; search. The low byte of ptr4 is already set.
|
||||
|
||||
@L3: sta ptr3
|
||||
lda ptr1+1
|
||||
sta ptr3+1
|
||||
lda ptr2+1
|
||||
sta ptr4+1
|
||||
ldy #1 ; First char is identical, so start on second
|
||||
|
||||
; Do the compare
|
||||
|
||||
@L4: lda (ptr4),y ; Get char from needle
|
||||
beq @Found ; Jump if end of needle (-> found)
|
||||
|
||||
jsr tolowerdirect ; Lowercase
|
||||
sta tmp2
|
||||
|
||||
lda (ptr3),y ; Compare with haystack
|
||||
|
||||
jsr tolowerdirect ; Lowercase
|
||||
cmp tmp2
|
||||
bne @L5 ; Jump if not equal
|
||||
iny ; Next char
|
||||
bne @L4
|
||||
inc ptr3+1
|
||||
inc ptr4+1 ; Bump hi byte of pointers
|
||||
bne @L4 ; Next char (branch always)
|
||||
|
||||
; The strings did not compare equal, search next start of needle
|
||||
|
||||
@L5: ldy #1 ; Start after this char
|
||||
bne @L1 ; Branch always
|
||||
|
||||
; We found the start of needle
|
||||
|
||||
@Found: lda ptr1
|
||||
ldx ptr1+1
|
||||
rts
|
||||
|
||||
; We reached end of haystack without finding needle
|
||||
|
||||
@NotFound:
|
||||
jmp return0 ; return NULL
|
@ -82,14 +82,3 @@ _strstr:
|
||||
lda #$00 ; return NULL
|
||||
tax
|
||||
rts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -10,19 +10,20 @@
|
||||
; int tolower (int c);
|
||||
;
|
||||
|
||||
.export _tolower
|
||||
.export _tolower, tolowerdirect
|
||||
.include "ctype.inc"
|
||||
.import ctypemaskdirect
|
||||
|
||||
_tolower:
|
||||
cpx #$00 ; out of range?
|
||||
bne @L2 ; if so, return the argument unchanged
|
||||
tay ; save char
|
||||
bne out ; if so, return the argument unchanged
|
||||
tolowerdirect:
|
||||
pha ; save char
|
||||
jsr ctypemaskdirect ; get character classification
|
||||
and #CT_UPPER ; upper case char?
|
||||
beq @L1 ; jump if no
|
||||
tya ; restore char
|
||||
pla ; restore char
|
||||
adc #<('a'-'A') ; make lower case char (ctypemaskdirect ensures carry clear)
|
||||
rts
|
||||
@L1: tya ; restore char
|
||||
@L2: rts
|
||||
@L1: pla ; restore char
|
||||
out: rts
|
||||
|
41
test/val/strstr-test.c
Normal file
41
test/val/strstr-test.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int fails = 0;
|
||||
|
||||
#define STRSTR_TEST(needle,expected) \
|
||||
if (strstr(haystack, (needle)) != (expected)) { \
|
||||
printf("strstr failure: expected %p for \"%s\", " \
|
||||
"got %p\n", \
|
||||
expected, needle, strstr(haystack, (needle)));\
|
||||
fails++; \
|
||||
}
|
||||
|
||||
#define STRCASESTR_TEST(needle,expected) \
|
||||
if (strcasestr(haystack, (needle)) != (expected)) { \
|
||||
printf("strcasestr failure: expected %p for \"%s\", " \
|
||||
"got %p\n", \
|
||||
expected, needle, strcasestr(haystack, (needle)));\
|
||||
fails++; \
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
const char *haystack = "This is a string to search in";
|
||||
|
||||
STRSTR_TEST("This is", haystack + 0);
|
||||
STRSTR_TEST("a string", haystack + 8);
|
||||
STRSTR_TEST("This is a string to search in", haystack);
|
||||
STRSTR_TEST("search in", haystack + 20);
|
||||
STRSTR_TEST("This is a string to search in with extra chars", NULL);
|
||||
STRSTR_TEST("nowhere", NULL);
|
||||
|
||||
STRCASESTR_TEST("this is", haystack + 0);
|
||||
STRCASESTR_TEST("a STRING", haystack + 8);
|
||||
STRCASESTR_TEST("this is a string TO search in", haystack);
|
||||
STRCASESTR_TEST("This is a string to search in with extra chars", NULL);
|
||||
STRCASESTR_TEST("search IN", haystack + 20);
|
||||
|
||||
return fails;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user