(defun match (pattern string)
(let ((i 0)
(len (length string))
star)
(loop
for c across pattern
while (< i len)
do
(case c
(#\* (setf star t))
(#\? (incf i))
(otherwise (if star
(loop while (and (< i len) (not (eql c (elt string i))))
do (incf i)
finally (incf i) (setf star nil))
(if (eql c (elt string i))
(incf i)
(return))))))
(and (= i len) (>= len (length pattern)))))
(format t "~a ==> ~a ~%" "abcd" (match "a*?b*c*d" "abcd"))
(loop :for (s r) :on '("abcd" nil
"aabcd" t
"a?bcd" t
"aa?bxcxd" t
"aa?bxcd" t) :by #'cddr
:for x := (match "a*?b*c*d" s) :do
(format t "~:[FAIL~;OK~] ~:S: expected ~:[false~;true~]~%" (eq r x) s r))
KGRlZnVuIG1hdGNoIChwYXR0ZXJuIHN0cmluZykKICAobGV0ICgoaSAwKQogICAgICAgIChsZW4gKGxlbmd0aCBzdHJpbmcpKQogICAgICAgIHN0YXIpCiAgICAobG9vcAogICAgICAgZm9yIGMgYWNyb3NzIHBhdHRlcm4KICAgICAgIHdoaWxlICg8IGkgbGVuKQogICAgICAgZG8KICAgICAgICAgKGNhc2UgYwogICAgICAgICAgICgjXCogKHNldGYgc3RhciB0KSkKICAgICAgICAgICAoI1w/IChpbmNmIGkpKQogICAgICAgICAgIChvdGhlcndpc2UgKGlmIHN0YXIKICAgICAgICAgICAgICAgICAgICAgICAgICAobG9vcCB3aGlsZSAoYW5kICg8IGkgbGVuKSAobm90IChlcWwgYyAoZWx0IHN0cmluZyBpKSkpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvIChpbmNmIGkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmluYWxseSAoaW5jZiBpKSAoc2V0ZiBzdGFyIG5pbCkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgKGlmIChlcWwgYyAoZWx0IHN0cmluZyBpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGluY2YgaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHJldHVybikpKSkpKQogICAgIChhbmQgKD0gaSBsZW4pICg+PSBsZW4gKGxlbmd0aCBwYXR0ZXJuKSkpKSkKCihmb3JtYXQgdCAifmEgPT0+IH5hIH4lIiAiYWJjZCIgKG1hdGNoICJhKj9iKmMqZCIgImFiY2QiKSkKCihsb29wIDpmb3IgKHMgcikgOm9uICcoImFiY2QiIG5pbAogICAgICAgICAgICAgICAgICAgICAgICJhYWJjZCIgdAogICAgICAgICAgICAgICAgICAgICAgICJhP2JjZCIgdAogICAgICAgICAgICAgICAgICAgICAgICJhYT9ieGN4ZCIgdAogICAgICAgICAgICAgICAgICAgICAgICJhYT9ieGNkIiB0KSA6YnkgIydjZGRyCiAgICA6Zm9yIHggOj0gKG1hdGNoICJhKj9iKmMqZCIgcykgOmRvCiAgICAoZm9ybWF0IHQgIn46W0ZBSUx+O09Lfl0gfjpTOiBleHBlY3RlZCB+OltmYWxzZX47dHJ1ZX5dfiUiIChlcSByIHgpIHMgcikp