Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 24 Oct 2005 00:20:05 +0200
From:      Roman Neuhauser <neuhauser@sigpipe.cz>
To:        freebsd-ports <freebsd-ports@freebsd.org>
Subject:   [RFC] make search extensions
Message-ID:  <20051023222005.GA1504@isis.sigpipe.cz>

next in thread | raw e-mail | index | archive | help

--OXfL5xGRrasGEqWY
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I'm collecting comments on the attached patch[1] (please refrain from
telling me to send-pr).

The patch pulls the awk body of the search target in
Mk/bsd.port.subdir.mk into Tools/make_search. I used the opportunity to
add comments (impossible with the embedded oneliner approach), and
tweaked the code in a few ways to make it more readable.

Contents:

* includes the change in
  http://www.freebsd.org/cgi/query-pr.cgi?pr=ports/87840
* adds the possibility to display a line only if the value is not
  empty (no more empty B-deps: or R-deps: lines)
  this is set by the user by appending a question mark to the display
  key, as in:

    make search display=name,path,bdeps\?

* adds conditional (see above) display of F-deps:, E-deps:, P-deps:
  lines
* enables searching on the F/E/P dependency fields
* the here/there/top spaghetti was replaced with equivalent code
  which better communicates the intent
* updates ports.7, plus misc. fixes to make the text more correct

[1] also at http://codex.sigpipe.cz/FreeBSD/ports/make_search,0.patch

-- 
How many Vietnam vets does it take to screw in a light bulb?
You don't know, man.  You don't KNOW.
Cause you weren't THERE.             http://bash.org/?255991

--OXfL5xGRrasGEqWY
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="make_search,0.patch"

Index: Mk/bsd.port.subdir.mk
===================================================================
RCS file: /home/ncvs/ports/Mk/bsd.port.subdir.mk,v
retrieving revision 1.60
diff -u -r1.60 bsd.port.subdir.mk
--- Mk/bsd.port.subdir.mk	28 Feb 2005 21:09:04 -0000	1.60
+++ Mk/bsd.port.subdir.mk	23 Oct 2005 21:02:12 -0000
@@ -327,106 +327,32 @@
 	PKGINSTALLVER="${PKGINSTALLVER:S/"/"'"'"/g:S/\$/\$\$/g:S/\\/\\\\/g}"
 .endif
 
-PORTSEARCH_DISPLAY_FIELDS?=name,path,info,maint,index,bdeps,rdeps,www
+PORTSEARCH_DISPLAY_FIELDS?=name,path,info,maint,cat,bdeps?,rdeps?,www,fdeps?,edeps?,pdeps?
 PORTSEARCH_KEYLIM?=0
 PORTSEARCH_XKEYLIM?=0
 PORTSEARCH_IGNORECASE?=1
+PORTSEARCH_DISPLAY_SHORT?=0
 
 search: ${PORTSDIR}/${INDEXFILE}
-	@here=${.CURDIR}; \
-	cd ${PORTSDIR}; \
-	if [ -z "$$key"   -a -z "$$xkey"   -a \
-	     -z "$$name"  -a -z "$$xname"  -a \
-	     -z "$$path"  -a -z "$$xpath"  -a \
-	     -z "$$info"  -a -z "$$xinfo"  -a \
-	     -z "$$maint" -a -z "$$xmaint" -a \
-	     -z "$$cat"   -a -z "$$xcat"   -a \
-	     -z "$$bdeps" -a -z "$$xbdeps" -a \
-	     -z "$$rdeps" -a -z "$$xrdeps" -a \
-	     -z "$$www"   -a -z "$$xwww"   ]; \
-	then \
-	  echo "The search target requires a keyword parameter or name parameter,"; \
-	  echo "e.g.: \"make search key=somekeyword\""; \
-	  echo "or    \"make search name=somekeyword\""; \
-	  exit; \
-	fi; \
-	awk -F\| -v there="$$here/" -v top="$$(pwd -P)" \
+	@test -n "$$display" && disp="$$display"; \
+	test -n "$$xdisplay" && xdisp="$$xdisplay"; \
+	awk -F\| -v pwd="${.CURDIR}/" -v portsdir="${PORTSDIR}" \
 	    -v key="$$key"          -v xkey="$$xkey" \
 	    -v name="$$name"        -v xname="$$xname" \
 	    -v path="$$path"        -v xpath="$$xpath" \
 	    -v info="$$info"        -v xinfo="$$xinfo" \
 	    -v maint="$$maint"      -v xmaint="$$xmaint" \
 	    -v cat="$$cat"          -v xcat="$$xcat" \
+	    -v fdeps="$$fdeps"      -v xfdeps="$$xfdeps" \
+	    -v edeps="$$edeps"      -v xedeps="$$xedeps" \
+	    -v pdeps="$$pdeps"      -v xpdeps="$$xpdeps" \
 	    -v bdeps="$$bdeps"      -v xbdeps="$$xbdeps" \
 	    -v rdeps="$$rdeps"      -v xrdeps="$$xrdeps" \
 	    -v www="$$www"          -v xwww="$$xwww" \
 	    -v icase="$${icase:-${PORTSEARCH_IGNORECASE}}" \
 	    -v keylim="$${keylim:-${PORTSEARCH_KEYLIM}}" \
 	    -v xkeylim="$${xkeylim:-${PORTSEARCH_XKEYLIM}}" \
-	    -v display="$${display:-${PORTSEARCH_DISPLAY_FIELDS}}" \
-	'BEGIN { \
-	    if (substr(there, 1, length(top)) == top) \
-	      there = "${PORTSDIR}" substr(there, 1 + length(top)); \
-	    therelen = length(there); \
-	    keylen = length(key); keylim = keylim && keylen; \
-	    if (!keylim && keylen) \
-	      parms[0] = (icase ? tolower(key) : key); \
-	    xkeylen = length(xkey); xkeylim = xkeylim && xkeylen; \
-	    if (!xkeylim && xkeylen) \
-	      xparms[0] = (icase ? tolower(xkey) : xkey); \
-		if (icase) { \
-	    if (length(name))  parms[1]  = tolower(name);  if (length(xname))  xparms[1]  = tolower(xname); \
-	    if (length(path))  parms[2]  = tolower(path);  if (length(xpath))  xparms[2]  = tolower(xpath); \
-	    if (length(info))  parms[4]  = tolower(info);  if (length(xinfo))  xparms[4]  = tolower(xinfo); \
-	    if (length(maint)) parms[6]  = tolower(maint); if (length(xmaint)) xparms[6]  = tolower(xmaint); \
-	    if (length(cat))   parms[7]  = tolower(cat);   if (length(xcat))   xparms[7]  = tolower(xcat); \
-	    if (length(bdeps)) parms[8]  = tolower(bdeps); if (length(xbdeps)) xparms[8]  = tolower(xbdeps); \
-	    if (length(rdeps)) parms[9]  = tolower(rdeps); if (length(xrdeps)) xparms[9]  = tolower(xrdeps); \
-	    if (length(www))   parms[10] = tolower(www);   if (length(xwww))   xparms[10] = tolower(xwww); \
-	  } else { \
-	    if (length(name))  parms[1]  = name;  if (length(xname))  xparms[1]  = xname; \
-	    if (length(path))  parms[2]  = path;  if (length(xpath))  xparms[2]  = xpath; \
-	    if (length(info))  parms[4]  = info;  if (length(xinfo))  xparms[4]  = xinfo; \
-	    if (length(maint)) parms[6]  = maint; if (length(xmaint)) xparms[6]  = xmaint; \
-	    if (length(cat))   parms[7]  = cat;   if (length(xcat))   xparms[7]  = xcat; \
-	    if (length(bdeps)) parms[8]  = bdeps; if (length(xbdeps)) xparms[8]  = xbdeps; \
-	    if (length(rdeps)) parms[9]  = rdeps; if (length(xrdeps)) xparms[9]  = xrdeps; \
-	    if (length(www))   parms[10] = www;   if (length(xwww))   xparms[10] = xwww; \
-	  } \
-	    fields["name"]  = 1;  names[1]  = "Port"; \
-	    fields["path"]  = 2;  names[2]  = "Path"; \
-	    fields["info"]  = 4;  names[4]  = "Info"; \
-	    fields["maint"] = 6;  names[6]  = "Maint"; \
-	    fields["cat"]   = 7;  names[7]  = "Index"; \
-	    fields["bdeps"] = 8;  names[8]  = "B-deps"; \
-	    fields["rdeps"] = 9;  names[9]  = "R-deps"; \
-	    fields["www"]   = 10; names[10] = "WWW"; \
-	    split(display, d, /,[ \t]*/); \
-	    for (i in d) { \
-	      disp[fields[d[i]]] = 1; \
-	    } \
-	  } \
-	  { \
-	    if (substr($$2, 1, therelen) != there) \
-	      next; \
-	    for (i in parms) \
-	      if ((icase ? tolower($$i) : $$i) !~ parms[i]) \
-	        next; \
-	    for (i in xparms) \
-	      if ((icase ? tolower($$i) : $$i) ~ xparms[i]) \
-	        next; \
-	    found = 0; \
-	    for (i = 1; i < 11; i++) \
-	      if (i in disp) { \
-	        if (xkeylim && (icase ? tolower($$i) : $$i) ~ xkey) \
-	          next; \
-	        if (!found && keylim && (icase ? tolower($$i) : $$i) ~ key) \
-	          found = 1; \
-	      } \
-	    if (keylim && !found) \
-	      next; \
-	    for (i = 1; i < 11; i++) \
-	      if (i in disp) \
-	        printf("%s:\t%s\n", names[i], $$i); \
-	    print(""); \
-	  }' ${PORTSDIR}/${INDEXFILE}
+	    -v disp="$${disp:-${PORTSEARCH_DISPLAY_FIELDS}}" \
+	    -v xdisp="$${xdisp}" \
+	    -v short="$${short:-${PORTSEARCH_DISPLAY_SHORT}}" \
+	    -f ${PORTSDIR}/Tools/make_search ${PORTSDIR}/${INDEXFILE}
Index: Tools/make_search
===================================================================
RCS file: Tools/make_search
diff -N Tools/make_search
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Tools/make_search	23 Oct 2005 21:19:23 -0000
@@ -0,0 +1,184 @@
+# This script is the body of the "make search" mechanism. It is easier to
+# use it through that interface than by hand. It accepts number of parameters
+# which can be divided into two groups:
+# - query parameters are described in ports(7)
+# - internals are described here
+#
+# pwd lets this script know where it's called from. Calling make search
+# inside a category directory limits the search to ports in that category,
+# which is otherwise equivalent to make search cat=${PWD:#$PORTSDIR/}
+#
+# portsdir is the canonical ${PORTSDIR}.
+#
+# The input fields are assumed to match the ordering in ${INDEXFILE}.
+# Patterns found in the parameters are matched against their respective fields
+# in ${INDEXFILE}.
+#
+# $FreeBSD$
+#
+BEGIN {
+  if (   0 == length(key)   && 0 == length(xkey) \
+      && 0 == length(name)  && 0 == length(xname) \
+      && 0 == length(path)  && 0 == length(xpath) \
+      && 0 == length(info)  && 0 == length(xinfo) \
+      && 0 == length(maint) && 0 == length(xmaint) \
+      && 0 == length(cat)   && 0 == length(xcat) \
+      && 0 == length(fdeps) && 0 == length(xfdeps) \
+      && 0 == length(edeps) && 0 == length(xedeps) \
+      && 0 == length(pdeps) && 0 == length(xpdeps) \
+      && 0 == length(bdeps) && 0 == length(xbdeps) \
+      && 0 == length(rdeps) && 0 == length(xrdeps) \
+      && 0 == length(www)   && 0 == length(xwww))
+  {
+    print "See ports(7) for a description of the search target.";
+    exit;
+  }
+  if (0 == length(pwd) || 0 == length(portsdir)) {
+    print "-v pwd=/path or -v portsdir=/path is missing"
+    exit;
+  }
+  "realpath " portsdir | getline realportsdir;
+  if (substr(pwd, 1, length(realportsdir)) == realportsdir) {
+    # this looks strange, but is valid concatenation
+    pwd = portsdir substr(pwd, 1 + length(realportsdir));
+  }
+  pwdlen = length(pwd);
+
+  # if keylim is false, we want to apply the key query to the full record.
+  # parms[0] will be matched against $0, which is good enough.
+  # same for xkeylim below.
+  # it keylim / xkeylim is set, we instead 
+  keylen = length(key);
+  keylim = keylim && keylen;
+  if (!keylim && keylen) {
+    parms[0] = (icase ? tolower(key) : key);
+  }
+  xkeylen = length(xkey);
+  xkeylim = xkeylim && xkeylen;
+  if (!xkeylim && xkeylen) {
+    xparms[0] = (icase ? tolower(xkey) : xkey);
+  }
+  # call tolower() once to save time
+  if (length(name))    parms[1]  = (icase ? tolower(name)   : name);
+  if (length(xname))  xparms[1]  = (icase ? tolower(xname)  : xname);
+  if (length(path))    parms[2]  = (icase ? tolower(path)   : path);
+  if (length(xpath))  xparms[2]  = (icase ? tolower(xpath)  : xpath);
+  if (length(info))    parms[4]  = (icase ? tolower(info)   : info);
+  if (length(xinfo))  xparms[4]  = (icase ? tolower(xinfo)  : xinfo);
+  if (length(maint))   parms[6]  = (icase ? tolower(maint)  : maint);
+  if (length(xmaint)) xparms[6]  = (icase ? tolower(xmaint) : xmaint);
+  if (length(cat))     parms[7]  = (icase ? tolower(cat)    : cat);
+  if (length(xcat))   xparms[7]  = (icase ? tolower(xcat)   : xcat);
+  if (length(bdeps))   parms[8]  = (icase ? tolower(bdeps)  : bdeps);
+  if (length(xbdeps)) xparms[8]  = (icase ? tolower(xbdeps) : xbdeps);
+  if (length(rdeps))   parms[9]  = (icase ? tolower(rdeps)  : rdeps);
+  if (length(xrdeps)) xparms[9]  = (icase ? tolower(xrdeps) : xrdeps);
+  if (length(www))     parms[10] = (icase ? tolower(www)    : www);
+  if (length(xwww))   xparms[10] = (icase ? tolower(xwww)   : xwww);
+  if (length(fdeps))   parms[11] = (icase ? tolower(fdeps)  : fdeps);
+  if (length(xfdeps)) xparms[11] = (icase ? tolower(xfdeps) : xfdeps);
+  if (length(edeps))   parms[12] = (icase ? tolower(edeps)  : edeps);
+  if (length(xedeps)) xparms[12] = (icase ? tolower(xedeps) : xedeps);
+  if (length(pdeps))   parms[13] = (icase ? tolower(pdeps)  : pdeps);
+  if (length(xpdeps)) xparms[13] = (icase ? tolower(xpdeps) : xpdeps);
+  name2pos["name"]  = 1;  pos2desc[1]  = "Port";
+  name2pos["path"]  = 2;  pos2desc[2]  = "Path";
+  name2pos["info"]  = 4;  pos2desc[4]  = "Info";
+  name2pos["maint"] = 6;  pos2desc[6]  = "Maint";
+  name2pos["cat"]   = 7;  pos2desc[7]  = "Index";
+  name2pos["bdeps"] = 8;  pos2desc[8]  = "B-deps";
+  name2pos["rdeps"] = 9;  pos2desc[9]  = "R-deps";
+  name2pos["www"]   = 10; pos2desc[10] = "WWW";
+  name2pos["fdeps"] = 11; pos2desc[11] = "F-deps";
+  name2pos["edeps"] = 12; pos2desc[12] = "E-deps";
+  name2pos["pdeps"] = 13; pos2desc[13] = "P-deps";
+
+  mandatory = 1;
+  optional  = 2;
+  # find out which fields should be displayed
+  split(disp, d, /,[ \t]*/);
+  for (key in d) {
+    dfv = mandatory;
+    spec = d[key];
+    # if the field ends with "?", it will be displayed only when nonempty
+    if ("?" == substr(spec, length(spec))) {
+      spec = substr(spec, 1, length(spec) - 1);
+      dfv = optional;
+    }
+    if (spec in name2pos) {
+      fldpos = name2pos[spec];
+      display_fields[fldpos] = dfv;
+    }
+  }
+  # find out which fields should be removed from the display
+  split(xdisp, d, /,[ \t]*/);
+  for (key in d) {
+    if (d[key] in name2pos) {
+      fldpos = name2pos[d[key]];
+      delete display_fields[fldpos];
+    }
+  }
+}
+{
+  # skip ports from other categories
+  if (substr($2, 1, pwdlen) != pwd) {
+    next;
+  }
+  # lowercase the values if icase is true
+  for (i = 1; i <= NF; i++) {
+      maybe_lower[i] = (icase ? tolower($i) : $i);
+  }
+  # each foo and xfoo query term is matched against the respective input field
+  # parms and xparms are already tolower()ed when icase is true.
+  # if !keylim, key is matched against $0;
+  # if !xkeylim, xkey is matched against $0.
+  #
+  # skip lines that don't match their inclusive query strings
+  for (pos in parms) {
+    if (maybe_lower[pos] !~ parms[pos]) {
+      next;
+    }
+  }
+  # skip lines that match their exclusive query strings
+  for (pos in xparms) {
+    if (maybe_lower[pos] ~ xparms[pos]) {
+      next;
+    }
+  }
+  # if keylim or xkeylim is in effect they need to be satisfied now.
+  if (xkeylim) {
+    for (pos in display_fields) {
+      if (maybe_lower[pos] ~ xkey) {
+        next;
+      }
+    }
+  }
+  # try to match key against any of the displayed fields
+  if (keylim) {
+    found = 0;
+    for (pos in display_fields) {
+      if (maybe_lower[pos] ~ key) {
+        found = 1;
+        break;
+      }
+    }
+    if (!found) {
+      next;
+    }
+  }
+  # display
+  for (pos = 1; pos <= NF; pos++) {
+    if (pos in display_fields) {
+      # skip fields that are optional and empty
+      if (optional == display_fields[pos] && $pos ~ /^[[:space:]]*$/) {
+        continue;
+      }
+      printf("%s:\t%s\n", pos2desc[pos], $pos);
+    }
+  }
+  if (!short) {
+    print("");
+  }
+}
+
+# vim: ft=awk
Index: share/man/man7/ports.7
===================================================================
RCS file: /home/ncvs/src/share/man/man7/ports.7,v
retrieving revision 1.52
diff -u -r1.52 ports.7
--- share/man/man7/ports.7	20 Jul 2005 22:22:53 -0000	1.52
+++ share/man/man7/ports.7	23 Oct 2005 20:55:07 -0000
@@ -256,36 +256,117 @@
 .It Cm search
 Search the
 .Pa INDEX
-file for the pattern specified by the
-.Va key
-(searches the port name, comment, and dependencies),
-.Va name
-(searches the port name only),
-.Va path
-(searches the port path),
-.Va info
-(searches the port info),
-.Va maint
-(searches the port maintainer),
-.Va cat
-(searches the port category),
-.Va bdeps
-(searches the port build-time dependency),
-.Va rdeps
-(searches the port run-time dependency)
+file for the pattern specified by the following parameters, listed along
+with port
 .Xr make 1
-variables, and their exclusion counterparts:
+macros they match (they're treated as awk(1) regular expressions):
+.Pp
+.Bl -tag -width ".Va maint" -compact
+.It Va name
+.Va PKGNAME
+.It Va path
+absolute port directory path
+.It Va info
+.Va COMMENT
+.It Va maint
+.Va MAINTAINER
+.It Va cat
+.Va CATEGORY
+.It Va fdeps
+.Va FETCH_DEPENDS
+.It Va edeps
+.Va EXTRACT_DEPENDS
+.It Va pdeps
+.Va PATCH_DEPENDS
+.It Va bdeps
+.Va BUILD_DEPENDS
+.It Va rdeps
+.Va RUN_DEPENDS
+.It Va www
+.Va MASTER_SITES
+.It Va key
+any of the above, but see keylim, xkeylim below
+.El
+.Pp
+All of these have exclusion counterparts:
 .Va xname , xkey
 etc.
+.Pp
+.Va display
+(or
+.Va disp )
+names the fields to display,
+Any field name may be appended with a question mark, such fields are displayed
+only when they are not empty.
+Default:
+.Qq name,path,info,maint,cat,bdeps?,rdeps?,www,fdeps?,edeps?,pdeps? .
+.Pa /etc/make.conf
+variable:
+.Va PORTSEARCH_DISPLAY_FIELDS .
+.Pp
+Note that the order of fields in output follows the order in
+.Pa INDEX .
+This might be changed to use the order in
+.Va display
+in the future.
+.Pp
+.Va xdisplay
+(or
+.Va xdisp )
+names the
+fields to remove from the (default) list of fields.
+.Pp
+.Va keylim
+defines the scope of
+.Va key .
+Possible values: 0, 1.
+When 0,
+.Va key
+is matched against the whole record; when 1,
+.Va key
+is only matched against displayed fields.
+Default: 0.
+.Pa /etc/make.conf
+variable:
+.Va PORTSEARCH_KEYLIM .
+.Pp
+.Va xkeylim
+defines the scope of
+.Va xkey .
+Possible values: 0, 1.
+When 0,
+.Va xkey
+is matched against the whole record; when 1,
+.Va xkey
+is only matched against displayed fields.
+Default: 0.
+.Pa /etc/make.conf
+variable:
+.Va PORTSEARCH_XKEYLIM .
+.Pp
+.Va icase
+toggles case sensitivity.
+Possible values: 0, 1.
+Default: 1.
+.Pa /etc/make.conf
+variable:
+.Va PORTSEARCH_IGNORECASE .
+.Pp
+.Va short
+toggles the output of the record separator (empty line).
+Possible values: 0, 1.
+Default: 0.
+.Pa /etc/make.conf
+variable:
+.Va PORTSEARCH_DISPLAY_SHORT .
+.Pp
 For example, one would type:
 .Pp
 .Dl "cd /usr/ports && make search name=query"
 .Pp
 to find all ports whose
-name matches
+names match
 .Dq Li query .
-Results include the matching ports' path, comment, maintainer,
-build dependencies, and run dependencies.
 .Bd -literal -offset indent
 cd /usr/ports && make search name=pear- \e
     xbdeps=apache
@@ -308,7 +389,7 @@
 or
 .Dq Li http .
 .Bd -literal -offset indent
-make search key=apache display=name,path,info keylim=1
+make search key=apache disp=name,path,info keylim=1
 .Ed
 .Pp
 To find ports that contain

--OXfL5xGRrasGEqWY--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20051023222005.GA1504>