Esempio n. 1
0
/**
 * XML <List> start element handler
 *
 * @see ListEHandler()
 *
 * @param array $attrs an array of key value pairs for the attributes
 */
function ListSHandler($attrs)
{
    global $gedrec, $repeats, $repeatBytes, $list, $repeatsStack, $processRepeats, $parser, $vars, $sortby;
    global $GEDCOM;
    $processRepeats++;
    if ($processRepeats > 1) {
        return;
    }
    $match = array();
    if (isset($attrs['sortby'])) {
        $sortby = $attrs['sortby'];
        if (preg_match("/\\\$(\\w+)/", $sortby, $match)) {
            $sortby = $vars[$match[1]]['id'];
            $sortby = trim($sortby);
        }
    } else {
        $sortby = "NAME";
    }
    if (isset($attrs['list'])) {
        $listname = $attrs['list'];
    } else {
        $listname = "individual";
    }
    // Some filters/sorts can be applied using SQL, while others require PHP
    switch ($listname) {
        case "pending":
            $rows = WT_DB::prepare("SELECT xref, gedcom_id, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" . " FROM `##change`" . " WHERE (xref, change_id) IN (" . "  SELECT xref, MAX(change_id)" . "   FROM `##change`" . "   WHERE status='pending' AND gedcom_id=?" . "   GROUP BY xref" . " )")->execute(array(WT_GED_ID))->fetchAll();
            $list = array();
            foreach ($rows as $row) {
                $list[] = WT_GedcomRecord::getInstance($row->xref, $row->gedcom_id, $row->gedcom);
            }
            break;
        case "individual":
        case "family":
            $sql_col_prefix = substr($listname, 0, 1) . "_";
            // i_ for individual, f_ for family, etc.
            $sql_join = array();
            $sql_where = array($sql_col_prefix . "file=" . WT_GED_ID);
            $sql_order_by = array();
            foreach ($attrs as $attr => $value) {
                if (strpos($attr, "filter") === 0 && $value) {
                    // Substitute global vars
                    $value = preg_replace_callback('/\\$(\\w+)/', function ($matches) use($vars) {
                        return $vars[$matches[1]]['id'];
                    }, $value);
                    // Convert the various filters into SQL
                    if (preg_match('/^(\\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
                        $sql_join[] = "JOIN `##dates` AS {$attr} ON ({$attr}.d_file={$sql_col_prefix}file AND {$attr}.d_gid={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}.d_fact='{$match[1]}'";
                        $date = new WT_Date($match[3]);
                        if ($match[2] == "LTE") {
                            $sql_where[] = "{$attr}.d_julianday2<=" . $date->minJD();
                        } else {
                            $sql_where[] = "{$attr}.d_julianday1>=" . $date->minJD();
                        }
                        if ($sortby == $match[1]) {
                            $sortby = "";
                            $sql_order_by[] = "{$attr}.d_julianday1";
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == "individual" && preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
                        // Do nothing, unless you have to
                        if ($match[1] != "" or $sortby == "NAME") {
                            $sql_join[] = "JOIN `##name` AS {$attr} ON (n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)";
                            // Search the DB only if there is any name supplied
                            if ($match[1] != "") {
                                $names = explode(" ", $match[1]);
                                foreach ($names as $name) {
                                    $sql_where[] = "{$attr}.n_full LIKE " . WT_DB::quote("%{$name}%");
                                }
                            }
                            // Let the DB do the name sorting even when no name was entered
                            if ($sortby == "NAME") {
                                $sortby = "";
                                $sql_order_by[] = "{$attr}.n_sort";
                            }
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == "individual" && preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) {
                        $sql_where[] = "i_gedcom REGEXP '" . $match[1] . "'";
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == "family" && preg_match('/^REGEXP \\/(.+)\\//', $value, $match)) {
                        $sql_where[] = "f_gedcom REGEXP '" . $match[1] . "'";
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == "family" && preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
                        // Eventually, family "names" will be stored in wt_name.  Until then, an extra is needed....
                        $sql_join[] = "JOIN `##link` AS {$attr}a ON ({$attr}a.l_file={$sql_col_prefix}file AND {$attr}a.l_from={$sql_col_prefix}id)";
                        $sql_join[] = "JOIN `##name` AS {$attr}b ON ({$attr}b.n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}a.l_type=IN ('HUSB, 'WIFE')";
                        $sql_where[] = "{$attr}.n_full LIKE " . WT_DB::quote("%{$match[1]}%");
                        if ($sortby == "NAME") {
                            $sortby = "";
                            $sql_order_by[] = "{$attr}.n_sort";
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif (preg_match('/^(?:\\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
                        $sql_join[] = "JOIN `##places` AS {$attr}a ON ({$attr}a.p_file={$sql_col_prefix}file)";
                        $sql_join[] = "JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}a.p_place LIKE " . WT_DB::quote("%{$match[1]}%");
                        // Don't unset this filter. This is just the first primary PLAC filter to reduce the returned list from the DB
                    } elseif ($listname == "individual" && preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) {
                        $query = "";
                        // Level 1 tag
                        if ($match[1] != "") {
                            $query .= "%1 {$match[1]}%";
                        }
                        // Level 2 tag
                        if ($match[2] != "") {
                            $query .= "%2 {$match[2]}%";
                        }
                        // Contains what?
                        if ($match[3] != "") {
                            $query .= "%{$match[3]}%";
                        }
                        $sql_where[] = "i_gedcom LIKE " . WT_DB::quote($query);
                    } elseif ($listname == "family" && preg_match('/^(\\w*):*(\\w*) CONTAINS (.+)$/', $value, $match)) {
                        $query = "";
                        // Level 1 tag
                        if ($match[1] != "") {
                            $query .= "%1 {$match[1]}%";
                        }
                        // Level 2 tag
                        if ($match[2] != "") {
                            $query .= "%2 {$match[2]}%";
                        }
                        // Contains what?
                        if ($match[3] != "") {
                            $query .= "%{$match[3]}%";
                        }
                        $sql_where[] = "f_gedcom LIKE " . WT_DB::quote($query);
                    } else {
                        // TODO: what other filters can we apply in SQL?
                    }
                }
            }
            if ($listname == "family") {
                $list = search_fams_custom($sql_join, $sql_where, $sql_order_by);
            } else {
                $list = search_indis_custom($sql_join, $sql_where, $sql_order_by);
            }
            // Clean up the SQL queries - they will not be used again
            unset($sql_join, $sql_where, $sql_order_by);
            break;
        default:
            die("Invalid list name: {$listname}");
    }
    $filters = array();
    $filters2 = array();
    if (isset($attrs['filter1']) and count($list) > 0) {
        foreach ($attrs as $key => $value) {
            if (preg_match("/filter(\\d)/", $key)) {
                $condition = $value;
                if (preg_match("/@(\\w+)/", $condition, $match)) {
                    $id = $match[1];
                    $value = "''";
                    if ($id == "ID") {
                        if (preg_match("/0 @(.+)@/", $gedrec, $match)) {
                            $value = "'" . $match[1] . "'";
                        }
                    } elseif ($id == "fact") {
                        $value = "'{$fact}'";
                    } elseif ($id == "desc") {
                        $value = "'{$desc}'";
                    } else {
                        if (preg_match("/\\d {$id} (.+)/", $gedrec, $match)) {
                            $value = "'" . str_replace("@", "", trim($match[1])) . "'";
                        }
                    }
                    $condition = preg_replace("/@{$id}/", $value, $condition);
                }
                //-- handle regular expressions
                if (preg_match("/([A-Z:]+)\\s*([^\\s]+)\\s*(.+)/", $condition, $match)) {
                    $tag = trim($match[1]);
                    $expr = trim($match[2]);
                    $val = trim($match[3]);
                    if (preg_match("/\\\$(\\w+)/", $val, $match)) {
                        $val = $vars[$match[1]]['id'];
                        $val = trim($val);
                    }
                    if ($val) {
                        $searchstr = "";
                        $tags = explode(":", $tag);
                        //-- only limit to a level number if we are specifically looking at a level
                        if (count($tags) > 1) {
                            $level = 1;
                            foreach ($tags as $t) {
                                if (!empty($searchstr)) {
                                    $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
                                }
                                //-- search for both EMAIL and _EMAIL... silly double gedcom standard
                                if ($t == "EMAIL" || $t == "_EMAIL") {
                                    $t = "_?EMAIL";
                                }
                                $searchstr .= $level . " " . $t;
                                $level++;
                            }
                        } else {
                            if ($tag == "EMAIL" || $tag == "_EMAIL") {
                                $tag = "_?EMAIL";
                            }
                            $t = $tag;
                            $searchstr = "1 " . $tag;
                        }
                        switch ($expr) {
                            case "CONTAINS":
                                if ($t == "PLAC") {
                                    $searchstr .= "[^\n]*[, ]*" . $val;
                                } else {
                                    $searchstr .= "[^\n]*" . $val;
                                }
                                $filters[] = $searchstr;
                                break;
                            default:
                                $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
                                break;
                        }
                    }
                }
            }
        }
    }
    //-- apply other filters to the list that could not be added to the search string
    if ($filters) {
        foreach ($list as $key => $record) {
            foreach ($filters as $filter) {
                if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(WT_USER_ACCESS_LEVEL))) {
                    unset($list[$key]);
                    break;
                }
            }
        }
    }
    if ($filters2) {
        $mylist = array();
        foreach ($list as $indi) {
            $key = $indi->getXref();
            $grec = $indi->privatizeGedcom(WT_USER_ACCESS_LEVEL);
            $keep = true;
            foreach ($filters2 as $filter) {
                if ($keep) {
                    $tag = $filter['tag'];
                    $expr = $filter['expr'];
                    $val = $filter['val'];
                    if ($val == "''") {
                        $val = "";
                    }
                    $tags = explode(":", $tag);
                    $t = end($tags);
                    $v = get_gedcom_value($tag, 1, $grec);
                    //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
                    if ($t == "EMAIL" && empty($v)) {
                        $tag = str_replace("EMAIL", "_EMAIL", $tag);
                        $tags = explode(":", $tag);
                        $t = end($tags);
                        $v = get_sub_record(1, $tag, $grec);
                    }
                    $level = count($tags);
                    switch ($expr) {
                        case "GTE":
                            if ($t == "DATE") {
                                $date1 = new WT_Date($v);
                                $date2 = new WT_Date($val);
                                $keep = WT_Date::Compare($date1, $date2) >= 0;
                            } elseif ($val >= $v) {
                                $keep = true;
                            }
                            break;
                        case "LTE":
                            if ($t == "DATE") {
                                $date1 = new WT_Date($v);
                                $date2 = new WT_Date($val);
                                $keep = WT_Date::Compare($date1, $date2) <= 0;
                            } elseif ($val >= $v) {
                                $keep = true;
                            }
                            break;
                        default:
                            if ($v == $val) {
                                $keep = true;
                            } else {
                                $keep = false;
                            }
                            break;
                    }
                }
            }
            if ($keep) {
                $mylist[$key] = $indi;
            }
        }
        $list = $mylist;
    }
    switch ($sortby) {
        case "NAME":
            uasort($list, array("WT_GedcomRecord", "compare"));
            break;
        case "CHAN":
            uasort($list, function (WT_GedcomRecord $x, WT_GedcomRecord $y) {
                $f1 = $x->getFirstFact('CHAN');
                $f2 = $y->getFirstFact('CHAN');
                if ($f1 && $f2) {
                    $d1 = $f1->getDate();
                    $d2 = $f2->getDate();
                    $cmp = WT_Date::compare($d1, $d2);
                    if ($cmp) {
                        return $cmp;
                    } else {
                        // Same date.  Compare times
                        preg_match('/\\n3 TIME (.+)/', $f1->getGedcom(), $m1);
                        preg_match('/\\n3 TIME (.+)/', $f2->getGedcom(), $m2);
                        return strcmp($m1[1], $m2[1]);
                    }
                } else {
                    return 0;
                }
            });
            break;
        case "BIRT:DATE":
            uasort($list, array("WT_Individual", "CompareBirtDate"));
            break;
        case "DEAT:DATE":
            uasort($list, array("WT_Individual", "CompareDeatDate"));
            break;
        case "MARR:DATE":
            uasort($list, array("WT_Family", "compareMarrDate"));
            break;
        default:
            // unsorted or already sorted by SQL
            break;
    }
    array_push($repeatsStack, array($repeats, $repeatBytes));
    $repeatBytes = xml_get_current_line_number($parser) + 1;
}
Esempio n. 2
0
/**
* @todo add info
* @see PGVRListEHandler()
* @param array $attrs an array of key value pairs for the attributes
*/
function PGVRListSHandler($attrs)
{
    global $pgvreport, $gedrec, $repeats, $repeatBytes, $list, $repeatsStack, $processRepeats, $parser, $vars, $sortby;
    global $pgv_changes, $GEDCOM, $TBLPREFIX;
    $processRepeats++;
    if ($processRepeats > 1) {
        return;
    }
    if (isset($attrs["sortby"])) {
        $sortby = $attrs["sortby"];
        $vmatch = array();
        if (preg_match("/\\\$(\\w+)/", $sortby, $vmatch) > 0) {
            $sortby = $vars[$vmatch[1]]["id"];
            $sortby = trim($sortby);
        }
    } else {
        $sortby = "NAME";
    }
    if (isset($attrs["list"])) {
        $listname = $attrs["list"];
    } else {
        $listname = "individual";
    }
    // Some filters/sorts can be applied using SQL, while others require PHP
    switch ($listname) {
        case 'pending':
            $list = array();
            foreach ($pgv_changes as $changes) {
                $change = end($changes);
                if ($change['gedcom'] == $GEDCOM) {
                    $list[] = new GedcomRecord($change['undo']);
                }
            }
            break;
        case 'individual':
        case 'family':
            $sql_col_prefix = substr($listname, 0, 1) . '_';
            // i_ for individual, f_ for family, etc.
            $sql_join = array();
            $sql_where = array($sql_col_prefix . "file=" . PGV_GED_ID);
            $sql_order_by = array();
            foreach ($attrs as $attr => $value) {
                if (strpos($attr, 'filter') === 0 && $value) {
                    // Substitute global vars
                    $value = preg_replace('/\\$(\\w+)/e', '$vars["\\1"]["id"]', $value);
                    // Convert the various filters into SQL
                    $match = array();
                    if (preg_match('/^(\\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
                        $sql_join[] = "JOIN {$TBLPREFIX}dates AS {$attr} ON ({$attr}.d_file={$sql_col_prefix}file AND {$attr}.d_gid={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}.d_fact='{$match[1]}'";
                        $date = new GedcomDate($match[3]);
                        if ($match[2] == 'LTE') {
                            $sql_where[] = "{$attr}.d_julianday2<=" . $date->minJD();
                        } else {
                            $sql_where[] = "{$attr}.d_julianday1>=" . $date->minJD();
                        }
                        if ($sortby == $match[1]) {
                            $sortby = '';
                            $sql_order_by[] = "{$attr}.d_julianday1";
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == 'individual' && preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
                        $sql_join[] = "JOIN {$TBLPREFIX}name AS {$attr} ON (n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}.n_sort LIKE '" . UTF8_strtoupper($match[1]) . "%'";
                        if ($sortby == 'NAME') {
                            $sortby = '';
                            $sql_order_by[] = "{$attr}.n_sort";
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                    } elseif ($listname == 'family' && preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
                        // Eventually, family "names" will be stored in pgv_name.  Until then, an extra is needed....
                        $sql_join[] = "JOIN {$TBLPREFIX}link AS {$attr}a ON ({$attr}a.l_file={$sql_col_prefix}file AND {$attr}a.l_from={$sql_col_prefix}id)";
                        $sql_join[] = "JOIN {$TBLPREFIX}name AS {$attr}b ON ({$attr}b.n_file={$sql_col_prefix}file AND n_id={$sql_col_prefix}id)";
                        $sql_where[] = "{$attr}a.l_type=IN ('HUSB, 'WIFE')";
                        $sql_where[] = "{$attr}.n_sort LIKE '" . UTF8_strtoupper($match[1]) . "%'";
                        if ($sortby == 'NAME') {
                            $sortby = '';
                            $sql_order_by[] = "{$attr}.n_sort";
                        }
                        unset($attrs[$attr]);
                        // This filter has been fully processed
                        //returns all the record with the filter place - does not verify that we speak of a BIRT record for the birth report value=BIRT:PLAC etc.
                        /* 				} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
                        					$sql_join[]="JOIN {$TBLPREFIX}places AS {$attr}a ON ({$attr}a.p_file={$sql_col_prefix}file)";
                        					$sql_join[]="JOIN {$TBLPREFIX}placelinks AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid={$sql_col_prefix}id)";
                        					$sql_where[]="{$attr}a.p_place LIKE '".UTF8_strtoupper($match[1])."%'";
                        					unset($attrs[$attr]); // This filter has been fully processed
                        */
                    } else {
                        // TODO: what other filters can we apply in SQL?
                        //var_dump($value);
                    }
                }
            }
            if ($listname == 'family') {
                $list = search_fams_custom($sql_join, $sql_where, $sql_order_by);
            } else {
                $list = search_indis_custom($sql_join, $sql_where, $sql_order_by);
            }
            break;
        default:
            die("Invalid list name: {$listname}");
    }
    $filters = array();
    $filters2 = array();
    if (isset($attrs["filter1"])) {
        $j = 0;
        foreach ($attrs as $key => $value) {
            $ct = preg_match("/filter(\\d)/", $key, $match);
            if ($ct > 0) {
                $condition = $value;
                $ct = preg_match("/@(\\w+)/", $condition, $match);
                if ($ct > 0) {
                    $id = $match[1];
                    $value = "''";
                    if ($id == "ID") {
                        $ct = preg_match("/0 @(.+)@/", $gedrec, $match);
                        if ($ct > 0) {
                            $value = "'" . $match[1] . "'";
                        }
                    } else {
                        if ($id == "fact") {
                            $value = "'{$fact}'";
                        } else {
                            if ($id == "desc") {
                                $value = "'{$desc}'";
                            } else {
                                $ct = preg_match("/\\d {$id} (.+)/", $gedrec, $match);
                                if ($ct > 0) {
                                    $value = "'" . preg_replace("/@/", "", trim($match[1])) . "'";
                                }
                            }
                        }
                    }
                    $condition = preg_replace("/@{$id}/", $value, $condition);
                }
                //-- handle regular expressions
                $ct = preg_match("/([A-Z:]+)\\s*([^\\s]+)\\s*(.+)/", $condition, $match);
                if ($ct > 0) {
                    $tag = trim($match[1]);
                    $expr = trim($match[2]);
                    $val = trim($match[3]);
                    if (preg_match("/\\\$(\\w+)/", $val, $vmatch) > 0) {
                        $val = $vars[$vmatch[1]]["id"];
                        $val = trim($val);
                    }
                    $searchstr = "";
                    $tags = explode(':', $tag);
                    //-- only limit to a level number if we are specifically looking at a level
                    if (count($tags) > 1) {
                        $level = 1;
                        foreach ($tags as $t) {
                            if (!empty($searchstr)) {
                                $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
                            }
                            //-- search for both EMAIL and _EMAIL... silly double gedcom standard
                            if ($t == "EMAIL" || $t == "_EMAIL") {
                                $t = "_?EMAIL";
                            }
                            $searchstr .= $level . " " . $t;
                            $level++;
                        }
                    } else {
                        if ($tag == "EMAIL" || $tag == "_EMAIL") {
                            $tag = "_?EMAIL";
                        }
                        $t = $tag;
                        $searchstr = "1 " . $tag;
                    }
                    switch ($expr) {
                        case "CONTAINS":
                            if ($t == "PLAC") {
                                $searchstr .= "[^\n]*[, ]" . $val;
                            } else {
                                $searchstr .= "[^\n]*" . $val;
                            }
                            $filters[] = $searchstr;
                            break;
                        default:
                            if (!empty($val)) {
                                $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
                            }
                            break;
                    }
                }
            }
            $j++;
        }
    }
    //-- apply other filters to the list that could not be added to the search string
    if ($filters) {
        foreach ($list as $key => $record) {
            foreach ($filters as $filter) {
                if (!preg_match('/' . $filter . '/i', $record->getGedcomRecord())) {
                    unset($list[$key]);
                    break;
                }
            }
        }
    }
    if ($filters2) {
        $mylist = array();
        foreach ($list as $indi) {
            $key = $indi->getXref();
            $value = $indi->getGedcomRecord();
            $keep = true;
            foreach ($filters2 as $filter) {
                if ($keep) {
                    $tag = $filter["tag"];
                    $expr = $filter["expr"];
                    $val = $filter["val"];
                    if ($val == "''") {
                        $val = "";
                    }
                    $tags = explode(':', $tag);
                    $t = end($tags);
                    $v = get_gedcom_value($tag, 1, $value["gedcom"], '', false);
                    //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
                    if ($t == "EMAIL" && empty($v)) {
                        $tag = preg_replace("/EMAIL/", "_EMAIL", $tag);
                        $tags = explode(':', $tag);
                        $t = end($tags);
                        $v = get_sub_record(1, $tag, $value["gedcom"]);
                    }
                    $level = count($tags);
                    switch ($expr) {
                        case "GTE":
                            if ($t == "DATE") {
                                $date1 = new GedcomDate($v);
                                $date2 = new GedcomDate($val);
                                $keep = GedcomDate::Compare($date1, $date2) >= 0;
                            } else {
                                if ($val >= $v) {
                                    $keep = true;
                                }
                            }
                            break;
                        case "LTE":
                            if ($t == "DATE") {
                                $date1 = new GedcomDate($v);
                                $date2 = new GedcomDate($val);
                                $keep = GedcomDate::Compare($date1, $date2) <= 0;
                            } else {
                                if ($val >= $v) {
                                    $keep = true;
                                }
                            }
                            break;
                        case "SUBCONTAINS":
                            $v = get_sub_record($level, $level . " " . $tag, $value["gedcom"]);
                            if (empty($v) && $tag == "ADDR") {
                                $v = get_sub_record($level + 1, $level + 1 . " " . $tag, $value["gedcom"]);
                            }
                            $ct = preg_match("/{$val}\\b/i", $v);
                            if ($ct > 0) {
                                $keep = true;
                            } else {
                                $keep = false;
                            }
                            break;
                        default:
                            if ($v == $val) {
                                $keep = true;
                            } else {
                                $keep = false;
                            }
                            break;
                    }
                }
            }
            if ($keep) {
                $mylist[$key] = $indi;
            }
        }
        $list = $mylist;
    }
    switch ($sortby) {
        case 'NAME':
            uasort($list, array('GedcomRecord', 'Compare'));
            break;
        case 'ID':
            uasort($list, array('GedcomRecord', 'CompareId'));
            break;
        case 'CHAN':
            uasort($list, array('GedcomRecord', 'CompareChanDate'));
            break;
        case 'BIRT:DATE':
            uasort($list, array('Person', 'CompareBirtDate'));
            break;
        case 'DEAT:DATE':
            uasort($list, array('Person', 'CompareDeatDate'));
            break;
        case 'MARR:DATE':
            uasort($list, array('Family', 'CompareMarrDate'));
            break;
        default:
            // unsorted or already sorted by SQL
            break;
    }
    array_push($repeatsStack, array($repeats, $repeatBytes));
    $repeatBytes = xml_get_current_line_number($parser) + 1;
}