fnmatches fails to match '?' to zero occurences

196 Views Asked by At

I'm writing a basic test for fnmatch before integrating in my code. Here is the test:

int main (int argc, char *argv[])
{
    for (int i = 2; i < argc; i++)
        if (!fnmatch(argv[1], argv[i], 0))
            printf("%s matches %s\n", argv[i], argv[1]);
        else
            printf("%s doesn't matches %s\n", argv[i], argv[1]);
    return EXIT_SUCCESS;
}

I compiled it to test and run the following command:

$ ./test a*b? a ab aa acb abc aabc acbc aaaaaaaaaaaaaaab aaaaaaaaaaaaaaaba abb aabb

Expexted output:

a doesn't matches a*b?
ab matches a*b?
aa doesn't matches a*b?
acb matches a*b?
abc matches a*b?
aabc matches a*b?
acbc matches a*b?
aaaaaaaaaaaaaaaba matches a*b?
aaaaaaaaaaaaaaab matches a*b?
abb matches a*b?
aabb matches a*b? 

Real output:

a doesn't matches a*b?
ab doesn't matches a*b?
aa doesn't matches a*b?
acb doesn't matches a*b?
abc matches a*b?
aabc matches a*b?
acbc matches a*b?
aaaaaaaaaaaaaaab doesn't matches a*b?
aaaaaaaaaaaaaaaba matches a*b?
abb matches a*b?
aabb matches a*b?

The problem being that the '?' metacharacter fails to match 0 characters (requiring exactly one).

Does anyone know why is it behaving this way and how to fix it?

Thank you in advance.

2

There are 2 best solutions below

6
On BEST ANSWER

This is expected behavior. Posix glob matches ? to a single character, not an empty group. See glob matching.

? can be used to match empty group in regular expressions, though - where it does match zero or one occurrence.

0
On

If using a Linux OS with the glibc implementation of fnmatch(), you can use the FNM_EXTMATCH flag to enable ksh/bash style extended globbing, where ?(pattern) matches pattern 1 or 0 times. Using that, ?(?) will match 1 or 0 instances of any character.

Example:

// Define before any includes to get FNM_EXTMATCH
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fnmatch.h>

int main (int argc, char *argv[])
{
    for (int i = 2; i < argc; i++)
        if (fnmatch(argv[1], argv[i], FNM_EXTMATCH) == 0)
            printf("%s matches %s\n", argv[i], argv[1]);
        else
            printf("%s doesn't match %s\n", argv[i], argv[1]);
    return EXIT_SUCCESS;
}

And usage (Tip: Put the pattern in quotes to prevent your shell from trying to expand it):

$ ./test  "a*b?(?)" a ab aa acb abc aabc acbc aaaaaaaaaaaaaaab aaaaaaaaaaaaaaaba abb aabb
a doesn't match a*b?(?)
ab matches a*b?(?)
aa doesn't match a*b?(?)
acb matches a*b?(?)
abc matches a*b?(?)
aabc matches a*b?(?)
acbc matches a*b?(?)
aaaaaaaaaaaaaaab matches a*b?(?)
aaaaaaaaaaaaaaaba matches a*b?(?)
abb matches a*b?(?)
aabb matches a*b?(?)