/*
**++
**  FACILITY:
**      NEWSSEARCH
**
**  ABSTRACT:
**      Implement the search command - Display item(s) containing a specified
**      target string.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990
**
**  Modification History:
**	6.1b8  24-Jan-1994  Mark Martinec  mark.martinec@ijs.si
**	  - replace calls to smg$put_chars with calls to smg_put_chars 
**	  - check status returned from all calls to SMG$
**	6.1b10 27-Apr-1995  Bob Sloane     sloane@kuhub.cc.ukans.edu
**	  - added Kent Covert's changes to search newsgroup names.
**	V6.1b11	21-Feb-1997     Mark Martinec   mark.martinec@ijs.si
**	  - fp_open is now int;  pass it by ref;
**	    don't try to delete_file_versions(fp_open);
**	V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace several calls to strcpy/strcat with safer strnncpy/strnncat
**--
**/

#ifdef vaxc
#module NEWSSEARCH "V6.2"
#endif

#define _NEWSSEARCH_C
#define module_name "NEWSSEARCH"

#include "newsinclude.h"
#include "newsextern.h"

extern int profile_search_val;

static 
int context = 0,
    cxtgrp,
    cxtitm,
    case_sensitive = 0,
    dheader = 0,
    dtpu = 0,
    display_call = 1,
    num_groups = 0,
    sea_groups = 0,
    stop_search = 0,
    tl[MAXTARGETS],
    tf[MAXTARGETS],
    tc,
    s_type = 0,
    sea_dir,
    last_search_level=3; /* 3=search articles, 1=search newsgroup names */
            
static
char targets[MAXTARGETS][512],
     target[512];

#define CLOSIZE	1
#define ESCAPE	'@'
#define DASH	'-'
#define CLOSURE '*'
#define ANCHOR  '+'
#define LITCHAR 'c'
#define NEWLINE	'\n'
#define BOL	'%'
#define EOL	'$'
#define ANY	'?'
#define CCL	'['
#define CCLEND	']'
#define NEGATE  '^'
#define NCCL	'!'
#define MAXPAT	512

static char
esc(aarg)
  char **aarg;
{
  if (**aarg != ESCAPE) return(**aarg);
  if (!(*((*aarg) + 1))) return(ESCAPE);
  ++(*aarg);
  if (**aarg == 'n') return('\n');
  if (**aarg == 't') return('\t');
  if (**aarg == 'f') return('\f');
  if (**aarg == 'r') return('\r');
  if (isdigit(**aarg)) {
    int lc, result = 0;
    for (lc=0; isdigit(**aarg) && (lc < 3); lc++) {
      result = (result * 10) + (**aarg - '0');
      ++(*aarg);
      }
    return(result);
    }
  return(**aarg);
}

static void
dodash(in_delim,aarg,apt)
  int in_delim;
  char **aarg,**apt;
{
  char *arg, k, delim = in_delim;

  while (**aarg && (**aarg != delim)) {
    arg = *aarg;

    if (*arg == ESCAPE) {
      **apt = esc(aarg);
      ++(*apt);
      }
    else if (*arg != DASH) {
      **apt = *arg;
      ++(*apt);
      }
    else if (isalnum(*(arg - 1)) && isalnum(*(arg + 1)) && (*(arg - 1) < *(arg + 1))) {
      for (k = *(arg - 1); k <= *(arg + 1) ; ++k) {
        **apt = k;
        ++(*apt);
        }
      ++(*aarg);
      }
    else {
      **apt = DASH;
      ++(*apt);
      }
    ++(*aarg);
    }
}

static int
getccl(aarg,apt)
  char **aarg,**apt;
{
  char *countpos;

  ++(*aarg);		/* skip over '[' */
  if (**aarg == NEGATE) {
    **apt = NCCL;
    ++(*apt);
    ++(*aarg);
    }
  else {
    **apt = CCL;
    ++(*apt);
    }
  countpos = *apt;
  *countpos = 0;
  ++(*apt);
  dodash(CCLEND,aarg,apt);
  *countpos = (*apt - countpos) - 1;
  return(**aarg == CCLEND);
}

int
makeregpat(arg,pat)
  char *arg, *pat;
{
  char *start = arg, *pt = pat, *lp, *llp = pat;

  while (*arg) {
    if (*arg == ANY) {llp = pt; *pt++ = ANY;}
    else if ((*arg == BOL) && (arg == start)) {llp=pt; *pt++ = BOL;}
    else if ((*arg == EOL) && (!*(arg+1))) {llp = pt; *pt++ = EOL;}
    else if (*arg == CCL) {
      llp = pt;
      if (!getccl(&arg,&pt)) return(0);
      }
    else if ((*arg == ANCHOR) && (arg > start)) {
      lp = llp;
      if ((*lp == BOL) || (*lp == EOL) || (*lp == CLOSURE)
          || (*lp == ANCHOR)) return(0);
      for (llp=pt; llp>=lp; llp--) *(llp+1) = *llp;
      *lp = ANCHOR;
      pt++;
      }
    else if ((*arg == CLOSURE) && (arg > start)) {
      lp = llp;
      if ((*lp == BOL) || (*lp == EOL) || (*lp == CLOSURE)
          || (*lp == ANCHOR)) return(0);
      for (llp=pt; llp>=lp; llp--) *(llp+1) = *llp;
      *lp = CLOSURE;
      pt++;
      }
    else {
      llp = pt;
      *pt++ = LITCHAR;
      *pt++ = esc(&arg);
      }
    ++arg;
    }
  *pt = '\0';
  return(1);
}

static char *start_pos;

static int
locate(in_c,pat)
  int in_c;
  char *pat;
{
  register char c = in_c, *cmp;
  for (cmp = pat + *pat;  cmp > pat;  cmp--) {
    if (*cmp == c) return(1);
    }
  return(0);

}

static int
omatch(alin,pat)
  char **alin, *pat;
{
  if (!**alin) return(0);
  switch (*pat) {
    case LITCHAR:
      if ((**alin) == *(pat + 1)) return(++(*alin) != 0);
      return(0);
    case BOL:
      if ((*alin) == start_pos) return(1);
      return(0);
    case ANY:
      if ((**alin) != NEWLINE) return(++(*alin) != 0);
      return(0);
    case EOL:
      if ((**alin) == NEWLINE) return(1);
      return(0);
    case CCL:
      if (locate(**alin,pat+1)) return(++(*alin) != 0);
      return(0);
    case NCCL:
      if (((**alin) != NEWLINE) && !locate(**alin,pat+1)) return(++(*alin) != 0);
      return(0);
    }
  return(0);
}

static int
patsize(pat)
  char *pat;
{
  switch (*pat) {
    case LITCHAR:
      return(2);
    case BOL:
    case EOL:
    case ANY:
      return(1);
    case CCL:
    case NCCL:
      return(*(pat+1) + 2);
    case CLOSURE:
      return(CLOSIZE);
    }
  return(1);
}

static int
amatch(lin,pat)
  char *lin, *pat;
{
  char *slin;

  while (*pat) {
    if (*pat == CLOSURE) {
      pat += patsize(pat);
      slin = lin;
      while (*lin)
        if (!omatch(&lin,pat)) break;

      for (; lin >= slin; lin--) {
        if (amatch(lin,pat)) return(1);
        }
      return(0);
      }
    else if (*pat == ANCHOR) {
      pat += patsize(pat);
      slin = lin;
      while (*lin)
        if (!omatch(&lin,pat)) break;

      for (; lin > slin; lin--) {
        if (amatch(lin,pat)) return(1);
        }
      return(0);
      }
    else if (!omatch(&lin,pat)) return(0);
    else pat += patsize(pat);
    }
  return(1);
}

static int
reg_match(lin,pat)
  char *lin, *pat;
{
  start_pos = lin;

  while (*lin && !amatch(lin,pat)) ++lin;
  return(*lin);
}

/*
 *  search_break
 *
 *  AST routine - keyboard interrupt
 */

void
search_break(pid,param,null_r0,null_r1,null_pc,null_psl)
  unsigned int pid,param,null_r0,null_r1,null_pc,null_psl;
{       
  stop_search = 1;
}

/*
 *  get_break_str
 *
 *  Read interrupt string and abort or continue search
 */

int get_break_str()
{
  int status;
  unsigned short trm;

  stop_search = 0;
  status = smg$read_keystroke(&kid,&trm,0,0,0,0,0);
  if (((status == SS$_ABORT) || (status == SS$_CANCEL)) && news_lock_alarm) {
    closefiles();
    exit(1);
    }
  status = get_input_general(&cmd_dsc,
                             c$dsc("NEWS> [RETURN to continue search] "),
                             &cmd_len,0,0,1,line_editing);
  if (((status == SS$_ABORT) || (status == SS$_CANCEL)) && news_lock_alarm) {
    closefiles();
    exit(1);
    }
  if (status == SMG$_EOF) status = RMS$_EOF;
  if (status == RMS$_EOF) {
    _c$cks(smg$disable_unsolicited_input(&pid));
    return(1);
    }
  else cmd[cmd_len] = '\0';
  if (cmd_len) {
    _c$cks(smg$disable_unsolicited_input(&pid));
    return(1);
    }
  if (!smg_active) printf("\tContinuing Search...\n");
  else {
    _c$cks(smg$end_pasteboard_update(&pid));
    smg_put_chars(trailer_vd,"Continuing Search...",2,1,1,SMG$M_REVERSE);
    _c$cks(smg$begin_pasteboard_update(&pid));
    }
  return(0);
}

/*
 *  exe_search
 *
 *  Locate next file containing target string and call display routine to
 *  file
 */

static int exe_search()
{
  int status;
  char ibuf[512], *s;
  int g, i, n_found, ti, all_found, fnum = 0, pdheader = 0;
  unsigned int sflags;
  GRP_PTR gap;
  ITM_PTR iap;
  int sea_start, sea_end, sea_step;

  context = 1;
  last_search_level = 3; /* searching articles, not groups */
  g = cxtgrp;
  gap = ga[g];
  if (!gap->grp_count) return(0);
  if (!gap->grp_ia) map_items(g);
  if (!gap->grp_count) return(0);
  iap = gap->grp_ia;
  sflags = gap->grp_flags;
  gap->grp_flags &= ~NEWS_M_NNTPCACHE;      /* turn off NNTP cache! */
/* 21-April-1993,  Martin Winter,  winter@vision.rs.ch          */
/* added handling for searching backwards (qualifier /REVERSE)  */
  if (sea_dir == SEARCH_BACKWARD)
     {
     sea_start = gap->grp_count;
     sea_end = 1;
     sea_step = -1;
     if (cxtitm == 0) {
       cxtitm = iap[gap->grp_count].itm_num;
       /* if we have a range, then we need to swap the start and end-nbr: */
       if ((d_itm[sea_groups].fitm > 0) && (d_itm[sea_groups].litm > 0)) {
         i = d_itm[sea_groups].fitm;
         d_itm[sea_groups].fitm = d_itm[sea_groups].litm;
         d_itm[sea_groups].litm = i;
         }
       }
     }
  else
     {
     sea_start = 1;
     sea_end = gap->grp_count;
     sea_step = 1;
     }
  for (i = sea_start; ((sea_dir == SEARCH_FORWARD) && (i <= sea_end)) ||
      ((sea_dir == SEARCH_BACKWARD) && (i >= sea_end)); i = i+sea_step) { 
    if (sea_dir == SEARCH_FORWARD) {
      if ( (iap[i].itm_num <= cxtitm)
        || (   (d_itm[sea_groups].fitm > 0)
            && (iap[i].itm_num < d_itm[sea_groups].fitm))) continue;
      if (d_itm[sea_groups].litm
          && (iap[i].itm_num > d_itm[sea_groups].litm)) break;
      }
    else {
      if ( (iap[i].itm_num >= cxtitm) 
        || (   (d_itm[sea_groups].fitm > 0)
            && (iap[i].itm_num > d_itm[sea_groups].fitm))) continue;
      if (d_itm[sea_groups].litm
          && (iap[i].itm_num < d_itm[sea_groups].litm)) break;
      }
/* end of changes */

    cxtitm = iap[i].itm_num;
    if (stop_search && get_break_str()) {
      gap->grp_flags = sflags;
      longjmp(env,2);
      }

    if (smg_active) {
      if (!(++fnum % 10)) {
        sprintf(err_oline,"Searching... %d items",fnum);
        err_line(err_oline);
        _c$cks(smg$end_pasteboard_update(&pid));
        _c$cks(smg$begin_pasteboard_update(&pid));
        }
      }
      
    if (stop_search && get_break_str()) {
      gap->grp_flags = sflags;
      longjmp(env,2);
      }

    if (!scan_filter(g,cxtitm)) continue;
    n_found = 1;
    itmptr.str_line = 0;
    for (ti = 0; ti < tc ; ++ti) tf[ti] = 0;
    if ((fp = do_open_item(g,i,"r",&fp_open)) != 0) {
      pdheader = 1;
      while (fgets(ibuf,sizeof(ibuf),fp)) {
        if (*ibuf == '\n') pdheader = dheader;
	++(itmptr.str_line);
        if (!case_sensitive) lower_case(ibuf);
        all_found = 1;
        for (ti = 0; ti < tc ; ++ti) {
          if (tf[ti]) continue;
          s = ibuf;
          n_found = 1;
          if (s_type == 1) {
	    if (wild_match(ibuf,targets[ti])) n_found = 0;
	    }
          else if (s_type == 2) {
            if (reg_match(ibuf,targets[ti])) n_found = 0;
	    }
	    else {
            while (n_found && ((s = strchr(s,*(targets[ti]))) != 0))
	      if ((n_found = strncmp(s,targets[ti],tl[ti])) != 0) ++s;
            }

          if (!n_found) {
            tf[ti] = 1;
            if (!ti) {
              if (s_type) {
	        itmptr.str_col = 1;
		s = chop_str(ibuf,'\n');
	        strnncpy(itmptr.str_target,sizeof(itmptr.str_target),
                         ibuf,-1, 0);
                itmptr.str_case_sensitive = case_sensitive;
                itmptr.str_length = strlen(ibuf);
                if (s) *s = '\n';
		}
	      else {
	        itmptr.str_col = (s - ibuf);
	        strnncpy(itmptr.str_target,sizeof(itmptr.str_target),
                         targets[0],-1, 0);
                itmptr.str_case_sensitive = case_sensitive;
                itmptr.str_length = tl[0];
		}
              }
            }
          else all_found = 0;
          }
        if (all_found) {
          n_found = 0;
          break;
          }
        n_found = 1;
        if (stop_search && get_break_str()) {
          fclose(fp);
       /* if (*fp_open > 1) delete_file_versions(fp_open); */
          gap->grp_flags = sflags;
          longjmp(env,2);
          }
        }
      fclose(fp);
   /* if (*fp_open > 1) delete_file_versions(fp_open); */
      }
    if (n_found) continue;
    gap->grp_flags = sflags;
    do_select("%",g,2);
    clear_err_line();
    cur_set_itm(curr_g,i);
    if ( smg_active ) _c$cks(smg$disable_unsolicited_input(&pid));
    if (display_call) {
      markasread(curr_g,curr_i,1);
      itmptr.str_searching = 1;
      do_display(pdheader,dtpu,0);
      itmptr.str_searching = 0;
      }
    return(1);
    }                     
  gap->grp_flags = sflags;
  return(0);
}

/*
 *  level1search
 *
 *  Locate next newsgroup that matches pattern.
 */

static int level1search()
{
  int group,increment,ti=0,n_found=0;

  if (sea_dir == SEARCH_BACKWARD) increment = -1;
  else increment = 1;
  if (cli$present(c$dsc("ALL")) & 1)
    if (sea_dir == SEARCH_BACKWARD) group = ga_size;
    else group = 1;
  else group = curr_g;

  if (context) group += increment;
  context = 1;
  while(group<=ga_size && group>=1 && !n_found) {
    if (ga[group]->grp_display_indx) {
      for (ti = 0; ti < tc; ++ti) {
        if (s_type == 1) n_found = wild_match(ga[group]->grp_name,targets[ti]);
        else if (s_type == 2) n_found = reg_match(ga[group]->grp_name,targets[ti]);
        else n_found = strstr(ga[group]->grp_name,targets[ti]) != 0;
        if (!n_found) break;
      }
    }
    if (!n_found) group+=increment;
  }

  if (n_found) {
    cur_set_grp(group);
    return 1;
  }
  return 0;
}

/*
 *  do_search
 *
 *  search command - arg/param extraction
 */

static int dsh()
{
  int ldheader = 1, ldtpu = 0, found = 0, i;
  char ngroup[GRPNAMELEN], s[80], *cp1, *cp2, *cps;
  unsigned short ngroup_len = 0, s_len;
  $DESCRIPTOR(ngroup_dsc,ngroup);
  $DESCRIPTOR(s_dsc,s);
  int status;

  if (cli$present(c$dsc("HEADER")) == CLI$_NEGATED) ldheader = 0;
  if (cli$present(c$dsc("TPU")) & 1) ldtpu = 1;
  if (!news_captive && (cli$present(c$dsc("EDIT")) & 1)) ldtpu = 1;
  if ((i = cli$present(c$dsc("DISPLAY"))) == CLI$_NEGATED) {
    i = 1;
    display_call = 0;
    }
  else if (i & 1) {
    i = 1;
    display_call = 1;
    }
  else i = 0;

  if ((news_context > 1 && last_search_level == 1) ||
     (news_context == 1 && last_search_level != 1))
       context = 0;

  if ((status=cli$get_value(c$dsc("TARGET"),&s_dsc,&s_len)) & 1) {
    cxtgrp = 0;
/* 21-April-1993,  Martin Winter,  winter@vision.rs.ch          */
/* added handling for searching backwards (qualifier /REVERSE)  */
    if (cli$present(c$dsc("REVERSE")) & 1)
       sea_dir = SEARCH_BACKWARD;
    else
       sea_dir = SEARCH_FORWARD;
/* end of enhancement */
    cxtitm = 0;
    context = 0;              
    dheader = ldheader;
    dtpu = ldtpu;
/* 22-April-1993,  Martin Winter,  winter@vision.rs.ch            */
/* SEARCH searched always the whole newsgroup instead (like /ALL  */
/* was specified), instead of using the current position as start */
/* Fixed to work as descibed in help and doc.                     */ 
    getnoterange();
    for (num_groups=0; d_itm[num_groups].fitm; num_groups++) {}
/* end of fix */
    s[s_len] = '\0';
    }
  else if (!context) {
    status = get_input_dflt(&s_dsc,c$dsc("Search String: "),&s_len,
      c$dsc(target),0);
    if (status == RMS$_EOF) return(0);
    s[s_len] = '\0';
    if (!i) display_call = 1;
    dheader = ldheader;
    dtpu = ldtpu;
/* 22-April-1993,  Martin Winter,  winter@vision.rs.ch            */
/* SEARCH searched always the whole newsgroup instead (like /ALL  */
/* was specified), instead of using the current position as start */
/* Fixed to work as descibed in help and doc.                     */
    getnoterange();
    for (num_groups=0; d_itm[num_groups].fitm; num_groups++) {}
/* end of fix */
    }

  if (cli$present(c$dsc("NEWSGROUPS")) & 1) {
    if (context) {
      context = 0;
      cxtgrp = 0;
/* 21-April-1993,  Martin Winter,  winter@vision.rs.ch          */
/* added handling for searching backwards (qualifier /REVERSE)  */
    if (cli$present(c$dsc("REVERSE")) & 1)
       sea_dir = SEARCH_BACKWARD;
    else
       sea_dir = SEARCH_FORWARD;
/* end of enhancement */
      cxtitm = 0;
      dheader = ldheader;
      dtpu = ldtpu;
      num_groups = 0;
      }
    if (cli$get_value(c$dsc("NEWSGROUPS"),&ngroup_dsc,&ngroup_len) & 1) {
      register int g;
      sea_groups = num_groups = 0;
      /* If we're told about a new list of newsgroups, then reset the search
   	 context.  Otherwise, use what we have... */
      for (g = 0; g < NOTE_RANGE_MAX; ++g) {
   	d_itm[g].fitm = 0    /* reset items in table at /NEWGROUPS*/;
        }
      do {
        ngroup[ngroup_len] = '\0';
        util_cvrt(ngroup,ngroup);
        for (g = 1; g <= ga_size && num_groups < NOTE_RANGE_MAX; ++g) {
          if (wild_match(ga[g]->grp_name,ngroup)) {
            d_itm[num_groups].fitm = -1;
            d_itm[num_groups++].ngrp = g;
            }
          }
        } while ((cli$get_value(c$dsc("NEWSGROUPS"),&ngroup_dsc,&ngroup_len)
            & 1)
          && (num_groups < 1000));
      }
    else num_groups = 0;
    }
  else {
    if ((cli$present(c$dsc("MARKER")) & 1) || (cli$present(c$dsc("NOTERANGE")) & 1)) {
      if (context) {
        context = 0;
        cxtgrp = 0;
/* 21-April-1993,  Martin Winter,  winter@vision.rs.ch          */
/* added handling for searching backwards (qualifier /REVERSE)  */
    if (cli$present(c$dsc("REVERSE")) & 1)
       sea_dir = SEARCH_BACKWARD;
    else
       sea_dir = SEARCH_FORWARD;
/* end of enhancement */
        cxtitm = 0;
        dheader = ldheader;
        dtpu = ldtpu;
/* 22-April-1993,  Martin Winter,  winter@vision.rs.ch            */
/* SEARCH searched always the whole newsgroup instead (like /ALL  */
/* was specified), instead of using the current position as start */
/* Fixed to work as descibed in help and doc.                     */
        }
/* end of fix */
      }
    for (num_groups=0; d_itm[num_groups].fitm; num_groups++) {}
    }
  get_filter(!context);

  if (cli$present(c$dsc("WILDCARD")) & 1) s_type = 1;
  else if (cli$present(c$dsc("PATTERN")) & 1) s_type = 2;
  else if (cli$present(c$dsc("LITERAL")) & 1) s_type = 0;
  else {
    if (!context) s_type = profile_search_val;
    }

  if (!context) {
    int case_s = 0;
    char *p;

    if (!s_len) {
      err_line("No search string specified");
      return(0);
      }
    for (p=s; *p; p++) {
      if (isupper(*p)) {
        if (!case_s) case_s = 1;
        else if (case_s != 1) break;
        }
      else if (islower(*p)) {
        if (!case_s) case_s = 2;
        else if (case_s != 2) break;
        }
      }
    if (!(case_sensitive = (*p))) lower_case(s);
    strnncpy(target,sizeof(target), s,-1, 0);
    tc = 0;
    for (cp1=target; cp1; cp1=cp2) {
/*
   2-SEP-1992, Martin Winter   (winter@vision.rs.ch)
   fix for bug with endless loop, if searchstring contains single & instead
   of &&. Because & is the documented way and && is the way for C-freaks,
   we allow both ways
*/
      if ( (cp2 = strchr(cp1,'&')) ) {
        *cp2 = '\0';
        cps = cp2 - 1;
        if (*(cp2 + 1) == '&')
           cp2 += 2;
        else
           cp2++;
/* end of fixed part */
        while (isspace(*cp2)) ++cp2;
        if (!*cp2) cp2 = 0;
        while (isspace(*cps) && (cps > cp1)) --cps;
        if (isspace(*cps)) continue;
        *(cps +1) = '\0';
        }
      if (tc < MAXTARGETS) {
	if (s_type == 1) sprintf(targets[tc],"*%.509s*",cp1);
	else if (s_type == 2) {
          if (!makeregpat(cp1,targets[tc])) --tc;
	  }
	else strnncpy(targets[tc],sizeof(targets[tc]), cp1,-1, 0);
        tl[tc] = strlen(cp1);
        }
      ++tc;
      }

    if (!tc) {
      err_line("Search - no search target loaded");
      return(0);
      }
    if (num_groups) sea_groups = 0;
    else if (curr_g) {
      sea_groups = 0;
      while (d_itm[num_groups].fitm) ++num_groups;
      }
    else num_groups = 0;
    if (!i) display_call = 1;
    }

  /* Is it a level 1 search? */
  if (news_context == 1 &&
      (!context || (context && last_search_level == 1))) {
      if (!(found = level1search())) {
	  context = 0;
	  err_line("Search string not located");
      }
      last_search_level = 1;
      return 0;
  }

  while (!found) {
    if (!context) {
      if (sea_groups < num_groups) {
        cxtgrp = d_itm[sea_groups].ngrp;
        cxtitm = 0;
        }
      else {
        if (!num_groups) err_line("No newsgroup specified");
        else err_line("Search string not located");
        return(0);
        }
      }
    else if (sea_groups < num_groups) 
      cxtgrp = d_itm[sea_groups].ngrp;
    stop_search = 0;
    if ( smg_active) _c$cks(smg$enable_unsolicited_input(&pid,search_break,0));
    if (!(found = exe_search())) context = 0;
    if (smg_active) _c$cks(smg$disable_unsolicited_input(&pid));
    ++sea_groups;
    }
  return(0);
}

int
do_search()
{
  return(unwind_display(OUTER_LOOP,dsh));
}
