Home | History | Annotate | Line # | Download | only in lib
      1 /*	$NetBSD: opattern.c,v 1.3 2021/04/10 19:49:59 nia Exp $	*/
      2 
      3 #if HAVE_CONFIG_H
      4 #include "config.h"
      5 #endif
      6 #include <nbcompat.h>
      7 #if HAVE_SYS_CDEFS_H
      8 #include <sys/cdefs.h>
      9 #endif
     10 __RCSID("$NetBSD: opattern.c,v 1.3 2021/04/10 19:49:59 nia Exp $");
     11 
     12 /*
     13  * FreeBSD install - a package for the installation and maintainance
     14  * of non-core utilities.
     15  *
     16  * Redistribution and use in source and binary forms, with or without
     17  * modification, are permitted provided that the following conditions
     18  * are met:
     19  * 1. Redistributions of source code must retain the above copyright
     20  *    notice, this list of conditions and the following disclaimer.
     21  * 2. Redistributions in binary form must reproduce the above copyright
     22  *    notice, this list of conditions and the following disclaimer in the
     23  *    documentation and/or other materials provided with the distribution.
     24  *
     25  * Jordan K. Hubbard
     26  * 18 July 1993
     27  *
     28  * Miscellaneous string utilities.
     29  *
     30  */
     31 
     32 #if HAVE_ASSERT_H
     33 #include <assert.h>
     34 #endif
     35 #if HAVE_ERR_H
     36 #include <err.h>
     37 #endif
     38 #if HAVE_FNMATCH_H
     39 #include <fnmatch.h>
     40 #endif
     41 #include "lib.h"
     42 #include "dewey.h"
     43 
     44 /* pull in definitions and macros for resizing arrays as we go */
     45 #include "defs.h"
     46 
     47 /*
     48  * Perform alternate match on "pkg" against "pattern",
     49  * calling pkg_match (recursively) to resolve any other patterns.
     50  * Return 1 on match, 0 otherwise
     51  */
     52 static int
     53 alternate_match(const char *pattern, const char *pkg)
     54 {
     55 	char   *sep;
     56 	char    buf[MaxPathSize];
     57 	char   *last;
     58 	char   *alt;
     59 	char   *cp;
     60 	int     cnt;
     61 	int     found;
     62 
     63 	if ((sep = strchr(pattern, '{')) == (char *) NULL) {
     64 		errx(EXIT_FAILURE, "alternate_match(): '{' expected in `%s'", pattern);
     65 	}
     66 	(void) strncpy(buf, pattern, (size_t) (sep - pattern));
     67 	alt = &buf[sep - pattern];
     68 	last = (char *) NULL;
     69 	for (cnt = 0, cp = sep; *cp && last == (char *) NULL; cp++) {
     70 		if (*cp == '{') {
     71 			cnt++;
     72 		} else if (*cp == '}' && --cnt == 0 && last == (char *) NULL) {
     73 			last = cp + 1;
     74 		}
     75 	}
     76 	if (cnt != 0) {
     77 		errx(EXIT_FAILURE, "Malformed alternate `%s'", pattern);
     78 	}
     79 	for (found = 0, cp = sep + 1; *sep != '}'; cp = sep + 1) {
     80 		for (cnt = 0, sep = cp; cnt > 0 || (cnt == 0 && *sep != '}' && *sep != ','); sep++) {
     81 			if (*sep == '{') {
     82 				cnt++;
     83 			} else if (*sep == '}') {
     84 				cnt--;
     85 			}
     86 		}
     87 		(void) snprintf(alt, sizeof(buf) - (alt - buf), "%.*s%s", (int) (sep - cp), cp, last);
     88 		if (pkg_match(buf, pkg) == 1) {
     89 			found = 1;
     90 		}
     91 	}
     92 	return found;
     93 }
     94 
     95 /*
     96  * Perform glob match on "pkg" against "pattern".
     97  * Return 1 on match, 0 otherwise
     98  */
     99 static int
    100 glob_match(const char *pattern, const char *pkg)
    101 {
    102 	return fnmatch(pattern, pkg, FNM_PERIOD) == 0;
    103 }
    104 
    105 /*
    106  * Perform simple match on "pkg" against "pattern".
    107  * Return 1 on match, 0 otherwise
    108  */
    109 static int
    110 simple_match(const char *pattern, const char *pkg)
    111 {
    112 	return strcmp(pattern, pkg) == 0;
    113 }
    114 
    115 /*
    116  * Performs a fast check if pattern can ever match pkg.
    117  * Returns 1 if a match is possible and 0 otherwise.
    118  */
    119 int
    120 quick_pkg_match(const char *pattern, const char *pkg)
    121 {
    122 #define simple(x) (isalnum((unsigned char)(x)) || (x) == '-')
    123 	if (!simple(pattern[0]))
    124 		return 1;
    125 	if (pattern[0] != pkg[0])
    126 		return 0;
    127 
    128 	if (!simple(pattern[1]))
    129 		return 1;
    130 	if (pattern[1] != pkg[1])
    131 		return 0;
    132 	return 1;
    133 #undef simple
    134 }
    135 
    136 /*
    137  * Match pkg against pattern, return 1 if matching, 0 else
    138  */
    139 int
    140 pkg_match(const char *pattern, const char *pkg)
    141 {
    142 	if (!quick_pkg_match(pattern, pkg))
    143 		return 0;
    144 
    145 	if (strchr(pattern, '{') != (char *) NULL) {
    146 		/* emulate csh-type alternates */
    147 		return alternate_match(pattern, pkg);
    148 	}
    149 	if (strpbrk(pattern, "<>") != (char *) NULL) {
    150 		int ret;
    151 
    152 		/* perform relational dewey match on version number */
    153 		ret = dewey_match(pattern, pkg);
    154 		if (ret < 0)
    155 			errx(EXIT_FAILURE, "dewey_match returned error");
    156 		return ret;
    157 	}
    158 	if (strpbrk(pattern, "*?[]") != (char *) NULL) {
    159 		/* glob match */
    160 		if (glob_match(pattern, pkg))
    161 			return 1;
    162 	}
    163 
    164 	/* no alternate, dewey or glob match -> simple compare */
    165 	if (simple_match(pattern, pkg))
    166 		return 1;
    167 
    168 	/* globbing patterns and simple matches may be specified with or
    169 	 * without the version number, so check for both cases. */
    170 
    171 	{
    172 		char *pattern_ver;
    173 		int retval;
    174 
    175 		pattern_ver = xasprintf("%s-[0-9]*", pattern);
    176 		retval = glob_match(pattern_ver, pkg);
    177 		free(pattern_ver);
    178 		return retval;
    179 	}
    180 }
    181 
    182 int
    183 pkg_order(const char *pattern, const char *first_pkg, const char *second_pkg)
    184 {
    185 	const char *first_version;
    186 	const char *second_version;
    187 
    188 	if (first_pkg == NULL && second_pkg == NULL)
    189 		return 0;
    190 
    191 	if (first_pkg == NULL)
    192 		return pkg_match(pattern, second_pkg) ? 2 : 0;
    193 	if (second_pkg == NULL)
    194 		return pkg_match(pattern, first_pkg) ? 1 : 0;
    195 
    196 	first_version = strrchr(first_pkg, '-');
    197 	second_version = strrchr(second_pkg, '-');
    198 
    199 	if (first_version == NULL || !pkg_match(pattern, first_pkg))
    200 		return pkg_match(pattern, second_pkg) ? 2 : 0;
    201 
    202 	if (second_version == NULL || !pkg_match(pattern, second_pkg))
    203 		return pkg_match(pattern, first_pkg) ? 1 : 0;
    204 
    205 	if (dewey_cmp(first_version + 1, DEWEY_GT, second_version + 1))
    206 		return 1;
    207 	else if (dewey_cmp(first_version + 1, DEWEY_LT, second_version + 1))
    208 		return 2;
    209 	else if (strcmp(first_pkg, second_pkg) < 0)
    210 		return 1;
    211 	else
    212 		return 2;
    213 }
    214