1  /*
       2   * fnmatch.c - Provide an interface to fnmatch(3) routine
       3   *
       4   * Arnold Robbins
       5   * arnold@skeeve.com
       6   * Written 7/2012
       7   */
       8  
       9  /*
      10   * Copyright (C) 2012, 2013, 2018 the Free Software Foundation, Inc.
      11   *
      12   * This file is part of GAWK, the GNU implementation of the
      13   * AWK Programming Language.
      14   *
      15   * GAWK is free software; you can redistribute it and/or modify
      16   * it under the terms of the GNU General Public License as published by
      17   * the Free Software Foundation; either version 3 of the License, or
      18   * (at your option) any later version.
      19   *
      20   * GAWK is distributed in the hope that it will be useful,
      21   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      22   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      23   * GNU General Public License for more details.
      24   *
      25   * You should have received a copy of the GNU General Public License
      26   * along with this program; if not, write to the Free Software
      27   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
      28   */
      29  
      30  #ifdef HAVE_CONFIG_H
      31  #include <config.h>
      32  #endif
      33  
      34  #include <stdio.h>
      35  #include <stdlib.h>
      36  #include <string.h>
      37  #include <unistd.h>
      38  
      39  #include <sys/stat.h>
      40  
      41  #if HAVE_SYS_SYSMACROS_H
      42  #include <sys/sysmacros.h>
      43  #elif HAVE_SYS_MKDEV_H
      44  #include <sys/mkdev.h>
      45  #endif /* HAVE_SYS_MKDEV_H */
      46  
      47  #include <sys/types.h>
      48  
      49  #include "gawkapi.h"
      50  
      51  #include "gettext.h"
      52  #define _(msgid)  gettext(msgid)
      53  #define N_(msgid) msgid
      54  
      55  #ifdef __VMS
      56  #define __iswctype iswctype
      57  #define __btowc btowc
      58  #endif
      59  
      60  #define _GNU_SOURCE	1	/* use GNU extensions if they're there */
      61  #ifdef HAVE_FNMATCH_H
      62  #include <fnmatch.h>
      63  #else
      64  #ifdef __VMS
      65  #include "fnmatch.h"	/* version that comes with gawk */
      66  #else
      67  #include "../missing_d/fnmatch.h"	/* version that comes with gawk */
      68  #endif
      69  #define HAVE_FNMATCH_H
      70  #endif
      71  
      72  #ifndef HAVE_FNMATCH
      73  #ifdef __VMS
      74  #include "fnmatch.c"	/* ditto */
      75  #else
      76  #include "../missing_d/fnmatch.c"	/* ditto */
      77  #endif
      78  #define HAVE_FNMATCH
      79  #endif
      80  
      81  /* Provide GNU extensions as no-ops if not defined */
      82  #ifndef FNM_CASEFOLD
      83  #define FNM_CASEFOLD	0
      84  #endif
      85  #ifndef FNM_LEADING_DIR
      86  #define FNM_LEADING_DIR	0
      87  #endif
      88  #ifndef FNM_FILE_NAME
      89  #define FNM_FILE_NAME	0
      90  #endif
      91  
      92  static const gawk_api_t *api;	/* for convenience macros to work */
      93  static awk_ext_id_t ext_id;
      94  static const char *ext_version = "fnmatch extension: version 1.0";
      95  
      96  static awk_bool_t init_fnmatch(void);
      97  static awk_bool_t (*init_func)(void) = init_fnmatch;
      98  
      99  int plugin_is_GPL_compatible;
     100  
     101  
     102  /* do_fnmatch --- implement the fnmatch interface */
     103  
     104  static awk_value_t *
     105  do_fnmatch(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     106  {
     107  #ifdef HAVE_FNMATCH_H
     108  	static int flags_mask =
     109  		FNM_CASEFOLD    | FNM_FILE_NAME |
     110  		FNM_LEADING_DIR | FNM_NOESCAPE |
     111  		FNM_PATHNAME    | FNM_PERIOD ;
     112  #endif
     113  	awk_value_t pattern, string, flags;
     114  	int int_flags, retval;
     115  
     116  	make_number(-1.0, result);	/* default return */
     117  
     118  #ifdef HAVE_FNMATCH
     119  	if (! get_argument(0, AWK_STRING, & pattern)) {
     120  		warning(ext_id, _("fnmatch: could not get first argument"));
     121  		goto out;
     122  	}
     123  
     124  	if (! get_argument(1, AWK_STRING, & string)) {
     125  		warning(ext_id, _("fnmatch: could not get second argument"));
     126  		goto out;
     127  	}
     128  
     129  	if (! get_argument(2, AWK_NUMBER, & flags)) {
     130  		warning(ext_id, _("fnmatch: could not get third argument"));
     131  		goto out;
     132  	}
     133  
     134  	int_flags = flags.num_value;
     135  	int_flags &= flags_mask;
     136  
     137  	retval = fnmatch(pattern.str_value.str,
     138  			string.str_value.str, int_flags);
     139  	make_number((double) retval, result);
     140  
     141  out:
     142  #else
     143  	fatal(ext_id, _("fnmatch is not implemented on this system\n"));
     144  #endif
     145  	return result;
     146  }
     147  
     148  #define ENTRY(x)	{ #x, FNM_##x }
     149  
     150  static struct fnmflags {
     151  	const char *name;
     152  	int value;
     153  } flagtable[] = {
     154  	ENTRY(CASEFOLD),
     155  	ENTRY(FILE_NAME),
     156  	ENTRY(LEADING_DIR),
     157  	ENTRY(NOESCAPE),
     158  	ENTRY(PATHNAME),
     159  	ENTRY(PERIOD),
     160  	{ NULL, 0 }
     161  };
     162  
     163  /* init_fnmatch --- load array with flags */
     164  
     165  static awk_bool_t
     166  init_fnmatch(void)
     167  {
     168  	int errors = 0;
     169  #ifdef HAVE_FNMATCH
     170  	awk_value_t index, value, the_array;
     171  	awk_array_t new_array;
     172  	int i;
     173  
     174  	if (! sym_update("FNM_NOMATCH", make_number(FNM_NOMATCH, & value))) {
     175  		warning(ext_id, _("fnmatch init: could not add FNM_NOMATCH variable"));
     176  		errors++;
     177  	}
     178  
     179  	new_array = create_array();
     180  	for (i = 0; flagtable[i].name != NULL; i++) {
     181  		(void) make_const_string(flagtable[i].name,
     182  				strlen(flagtable[i].name), & index);
     183  		(void) make_number(flagtable[i].value, & value);
     184  		if (! set_array_element(new_array, & index, & value)) {
     185  			warning(ext_id, _("fnmatch init: could not set array element %s"),
     186  					flagtable[i].name);
     187  			errors++;
     188  		}
     189  	}
     190  
     191  	the_array.val_type = AWK_ARRAY;
     192  	the_array.array_cookie = new_array;
     193  
     194  	if (! sym_update("FNM", & the_array)) {
     195  		warning(ext_id, _("fnmatch init: could not install FNM array"));
     196  		errors++;
     197  	}
     198  
     199  #endif
     200  	return errors == 0;
     201  }
     202  
     203  static awk_ext_func_t func_table[] = {
     204  	{ "fnmatch", do_fnmatch, 3, 3, awk_false, NULL },
     205  };
     206  
     207  /* define the dl_load function using the boilerplate macro */
     208  
     209  dl_load_func(func_table, fnmatch, "")