Index: /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/Makefile
===================================================================
--- /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/Makefile	(revision 33848)
+++ /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/Makefile	(revision 33849)
@@ -44,4 +44,5 @@
 $(SRC)/quit.$(ARCH).o	     \
 $(SRC)/run_for.$(ARCH).o    \
+$(SRC)/run_foreach.$(ARCH).o    \
 $(SRC)/run_if.$(ARCH).o     \
 $(SRC)/run_while.$(ARCH).o  \
Index: /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/init.c
===================================================================
--- /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/init.c	(revision 33848)
+++ /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/init.c	(revision 33849)
@@ -31,4 +31,5 @@
 int quit            PROTO((int, char **));
 int run_for         PROTO((int, char **));
+int run_foreach     PROTO((int, char **));
 int run_if          PROTO((int, char **));
 int run_while       PROTO((int, char **));
@@ -81,4 +82,5 @@
   {1, "quit",          quit,               "exit program *"},
   {1, "for",           run_for,            "for loop"}, 
+  {1, "foreach",       run_foreach,        "foreach loop"}, 
   {1, "if",            run_if,             "logical cases *"}, 
   {1, "while",         run_while,          "while loop"}, 
Index: /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/run_foreach.c
===================================================================
--- /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/run_foreach.c	(revision 33849)
+++ /branches/eam_branches/ipp-20120405/Ohana/src/opihi/cmd.basic/run_foreach.c	(revision 33849)
@@ -0,0 +1,114 @@
+# include "basic.h"
+# define D_NLINES 100
+static char prompt[] = ">> ";
+
+int run_foreach (int argc, char **argv) {
+
+  int ThisList, depth, i, done, status, NLINES, j;
+  char *input;
+  Macro loop;
+
+  if (argc < 2) {
+    gprint (GP_ERR, "USAGE: foreach (var) value [value value ...] -- terminate with 'END'\n");
+    return (FALSE);
+  }
+
+  NLINES = D_NLINES;
+  ALLOCATE (loop.line, char *, NLINES);
+
+  /* read in loop */
+  depth = 0;
+  ThisList = current_list_depth();
+  for (i = 0, done = FALSE; !done; ) {
+
+    /* get the next line (from correct place) */
+    if (ThisList == 0) 
+      input = opihi_readline (prompt);
+    else 
+      input = get_next_listentry (ThisList);
+    stripwhite (input);
+
+    /* check for end-of-data syntax error */
+    if (input == NULL) {
+      if (ThisList == 0)  {
+	gprint (GP_ERR, "end loop with 'END'\n");
+	continue;
+      } else {
+	gprint (GP_ERR, "misbalanced loop\n");
+	for (j = 0; j < loop.Nlines; j++) {
+	  free (loop.line[j]);
+	}
+	free (loop.line);
+	return (FALSE);
+      }	
+    }
+
+    /* test for new macro (or other list, in the future?) */
+    if (is_list (input)) depth ++;
+
+    /* test for end of nested list -- if not nested, END refers to this macro */
+    if (!strncasecmp (input, "END", 3)) {
+      depth --;
+      if (depth < 0) { 
+	free (input);
+	break;
+      }
+    }
+
+    /* if line has data, add to loop list */
+    if (*input) { 
+      loop.line[i] = input;
+      i++;
+      if (i == NLINES - 1) {
+	NLINES += D_NLINES;
+	REALLOCATE (loop.line, char *, NLINES);
+      }
+    }
+  }
+
+  /* cleanup loop data */
+  loop.Nlines = i;
+  REALLOCATE (loop.line, char *, MAX (loop.Nlines, 1));
+
+  status = TRUE;
+  interrupt = FALSE;
+  for (i = 2; (i < argc) && !interrupt; i++) {
+    set_str_variable (argv[1], argv[i]);
+    status = exec_loop (&loop);
+    if (loop_next) continue;
+    if (loop_last) break;
+    if (loop_break) break;
+  }
+  /* 'last' and 'next' should only affect one loop */
+  loop_last = loop_next = FALSE; 
+
+  /* break should propagate up if auto_break is set */
+  loop_break = FALSE;
+  if (auto_break && !status) loop_break = TRUE;
+
+  /* cleanup list */
+  for (j = 0; j < loop.Nlines; j++) {
+    free (loop.line[j]);
+  }
+  free (loop.line);
+
+  if (loop_break) return (FALSE);
+  return (TRUE);
+}
+
+/*
+  If we are entering at the keyboard (ThisList == 0), use readline.
+  Otherwise, read from the current list, remove list lines.
+  execute when we hit the final "END" (the true END -- we count macro defines!!) 
+*/
+     
+/* while processing the loop, the loop status variables may be set
+   by the loop commands, or the loop may quite on a failed command,
+   setting the exec_loop return status to false. the loop status variables
+   are:
+   loop_next : stop this loop, but try another loop
+   loop_last : stop loop processing, but return true so external loop may continue
+   loop_break : stop loop processing, and return false so external loop will break
+   interrupt : external interrupt signal
+*/
+
Index: /branches/eam_branches/ipp-20120405/Ohana/src/opihi/lib.shell/ListOps.c
===================================================================
--- /branches/eam_branches/ipp-20120405/Ohana/src/opihi/lib.shell/ListOps.c	(revision 33848)
+++ /branches/eam_branches/ipp-20120405/Ohana/src/opihi/lib.shell/ListOps.c	(revision 33849)
@@ -116,4 +116,17 @@
   
   status = !strcmp (comm, "for");
+  free (comm);
+  return (status);
+}
+
+int is_foreach_loop (char *line) {
+
+  int status;
+  char *comm;
+
+  comm = thisword (line);
+  if (comm == (char *) NULL) return (FALSE);
+  
+  status = !strcmp (comm, "foreach");
   free (comm);
   return (status);
@@ -266,4 +279,5 @@
   status |= is_macro_create (line);
   status |= is_for_loop (line);
+  status |= is_foreach_loop (line);
   status |= is_list_data (line);
   status |= is_loop (line);
