From 55c3a7d9b2bffa53b519be5a319565e311f8d7e6 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 17 Sep 2013 12:15:23 +0100
Subject: [PATCH] Implement url_exists, file_contains_string,
 url_contains_string.

The URL functions use the external 'curl' program.
---
 configure.ac |  6 ++++++
 goaljobs.ml  | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
 goaljobs.mli | 27 ++++++++++++++++++++++-----
 3 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/configure.ac b/configure.ac
index 7097795..d0dcf82 100644
--- a/configure.ac
+++ b/configure.ac
@@ -82,6 +82,12 @@ if test "x$OCAMLFIND" = "xno"; then
     AC_MSG_ERROR([You must install OCaml findlib (the ocamlfind command)])
 fi
 
+dnl Check for curl (for URL testing, downloads).
+AC_CHECK_PROG(CURL,curl,curl)
+if test "x$CURL" = "x"; then
+    AC_MSG_ERROR([You must install the 'curl' program])
+fi
+
 dnl Check for POD (for manual pages).
 AC_CHECK_PROG(PERLDOC,perldoc,perldoc)
 if test "x$PERLDOC" = "x"; then
diff --git a/goaljobs.ml b/goaljobs.ml
index 53db37c..4f99de4 100644
--- a/goaljobs.ml
+++ b/goaljobs.ml
@@ -62,7 +62,44 @@ let more_recent objs srcs =
     ) objs
   )
 
-let url_exists url = goal_failed "url_exists not implemented!"
+let url_exists url =
+  (* http://stackoverflow.com/questions/12199059/how-to-check-if-an-url-exists-with-the-shell-and-probably-curl *)
+  let cmd =
+    sprintf "curl --output /dev/null --silent --head --fail %s" (quote url) in
+  match Sys.command cmd with
+  | 0 -> true
+  | 1 -> false
+  | r ->
+    let msg = sprintf "curl error testing '%s' (exit code %d)" url r in
+    goal_failed msg
+
+let file_contains_string filename str =
+  let cmd = sprintf "grep -q %s %s" (quote str) (quote filename) in
+  match Sys.command cmd with
+  | 0 -> true
+  | 1 -> false
+  | r ->
+    let msg = sprintf "grep error testing for '%s' in '%s' (exit code %d)"
+      str filename r in
+    goal_failed msg
+
+let url_contains_string url str =
+  let tmp = Filename.temp_file "goaljobsurl" "" in
+  let cmd =
+    sprintf "curl --output %s --silent --fail %s" (quote tmp) (quote url) in
+  (match Sys.command cmd with
+  | 0 -> ()
+  | 1 ->
+    let msg = sprintf "curl failed to download URL '%s'" url in
+    goal_failed msg
+  | r ->
+    let msg = sprintf "curl error testing '%s' (exit code %d)" url r in
+    goal_failed msg
+  );
+  let r = file_contains_string tmp str in
+  unlink tmp;
+  r
+
 
 let sh fs =
   let do_sh cmd =
@@ -156,6 +193,16 @@ let goal_url_exists url =
     let msg = sprintf "url_exists: URL '%s' required but does not exist" url in
     goal_failed msg
   )
+let goal_file_contains_string filename str =
+  if not (file_contains_string filename str) then (
+    let msg = sprintf "file_contains_string: file '%s' is required to contain string '%s'" filename str in
+    goal_failed msg
+  )
+let goal_url_contains_string url str =
+  if not (url_contains_string url str) then (
+    let msg = sprintf "url_contains_string: URL '%s' is required to contain string '%s'" url str in
+    goal_failed msg
+  )
 let goal_memory_exists k =
   if not (memory_exists k) then (
     let msg = sprintf "memory_exists: key '%s' required but does not exist" k in
diff --git a/goaljobs.mli b/goaljobs.mli
index 8b31abe..07d717b 100644
--- a/goaljobs.mli
+++ b/goaljobs.mli
@@ -127,7 +127,9 @@ val file_exists : string -> bool
 val file_newer_than : string -> string -> bool
   (** [file_newer_than file_a file_b] returns true if [file_a] is
       newer than [file_b].  Note that if [file_a] does not exist, it
-      returns false.  If [file_b] does not exist, it is an error. *)
+      returns false.  If [file_b] does not exist, it is an error.
+
+      There is also a goal version of this function. *)
 
 val more_recent : string list -> string list -> bool
   (** [more_recent objects sources] expresses the [make] relationship:
@@ -148,14 +150,27 @@ val more_recent : string list -> string list -> bool
       Note that both parameters are lists (since in [make] you can
       have a list of source files and a list of object files).  If you
       don't want a list, pass a single-element list containing the
-      single the object/source file. *)
+      single the object/source file.
+
+      There is also a goal version of this function. *)
 
 val url_exists : string -> bool
   (** The URL is tested to see if it exists.
 
-      This function also exists as a goal.  Writing:
-      {v require (url_exists "http://example.com");}
-      will die unless the given URL exists. *)
+      There is also a goal version of this function. *)
+
+val file_contains_string : string -> string -> bool
+  (** [file_contains_string filename str] checks if the named file
+      contains the given substring [str].
+
+      There is also a goal version of this function. *)
+
+val url_contains_string : string -> string -> bool
+  (** [url_contains_string url str] downloads the URL and checks
+      whether the content contains the given substring [str].
+
+      There is also a goal version of this function. *)
+
 
 (** {2 Shell}
 
@@ -357,6 +372,8 @@ val goal_file_exists : string -> unit
 val goal_file_newer_than : string -> string -> unit
 val goal_more_recent : string list -> string list -> unit
 val goal_url_exists : string -> unit
+val goal_file_contains_string : string -> string -> unit
+val goal_url_contains_string : string -> string -> unit
 val goal_memory_exists : string -> unit
 
 (* A single call to this function is added by the 'goaljobs' script.
-- 
GitLab