/* Tracker - indexer and metadata database engine
* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc@gnome.org)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <zlib.h>
#include "tracker-dbus.h"
#include "tracker-utils.h"
#include "tracker-indexer.h"
#include "tracker-stemmer.h"
#include "../xdgmime/xdgmime.h"
#include "tracker-os-dependant.h"
#ifdef OS_WIN32
#include <conio.h>
#include "mingw-compat.h"
#else
#include <sys/resource.h>
#endif
extern Tracker *tracker;
char *tracker_actions[] = {
"TRACKER_ACTION_IGNORE", "TRACKER_ACTION_CHECK", "TRACKER_ACTION_DELETE", "TRACKER_ACTION_DELETE_SELF", "TRACKER_ACTION_CREATE","TRACKER_ACTION_MOVED_FROM",
"TRACKER_ACTION_MOVED_TO","TRACKER_ACTION_FILE_CHECK", "TRACKER_ACTION_FILE_CHANGED","TRACKER_ACTION_FILE_DELETED", "TRACKER_ACTION_FILE_CREATED",
"TRACKER_ACTION_FILE_MOVED_FROM", "TRACKER_ACTION_FILE_MOVED_TO", "TRACKER_ACTION_WRITABLE_FILE_CLOSED","TRACKER_ACTION_DIRECTORY_CHECK",
"TRACKER_ACTION_DIRECTORY_CREATED","TRACKER_ACTION_DIRECTORY_DELETED","TRACKER_ACTION_DIRECTORY_MOVED_FROM","TRACKER_ACTION_DIRECTORY_MOVED_TO",
"TRACKER_ACTION_DIRECTORY_REFRESH", "TRACKER_ACTION_EXTRACT_METADATA",
NULL};
char *ignore_suffix[] = {"~", ".o", ".la", ".lo", ".loT", ".in", ".csproj", ".m4", ".rej", ".gmo", ".orig", ".pc", ".omf", ".aux", ".tmp", ".po", NULL};
char *ignore_prefix[] = {"autom4te", "conftest.", "confstat", "config.", NULL};
char *ignore_name[] = {"po", "CVS", "aclocal", "Makefile", "CVS", "SCCS", "ltmain.sh","libtool", "config.status", "conftest", "confdefs.h", NULL};
#define ZLIBBUFSIZ 8192
#define TEXT_SNIFF_SIZE 4096
static int info_allocated = 0;
static int info_deallocated = 0;
static const char *months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static const char imonths[] = {
'1', '2', '3', '4', '5',
'6', '7', '8', '9', '0', '1', '2'
};
static Matches tmap[] = {
{"da", "danish"},
{"nl", "dutch"},
{"en", "english"},
{"fi", "finnish"},
{"fr", "french"},
{"de", "german"},
{"it", "italian"},
{"nb", "norwegian"},
{"pt", "portuguese"},
{"ru", "russian"},
{"es", "spanish"},
{"sv", "swedish"},
{NULL, NULL},
};
char *
tracker_get_service_by_id (int service_type_id)
{
char *str_id;
ServiceDef *def;
str_id = tracker_int_to_str (service_type_id);
def = g_hash_table_lookup (tracker->service_id_table, str_id);
g_free (str_id);
if (!def) {
tracker_log ("WARNING: no service found for id %d", service_type_id);
return NULL;
}
return g_strdup (def->name);
}
char *
tracker_get_parent_service_by_id (int service_type_id)
{
char *str_id = tracker_int_to_str (service_type_id);
ServiceDef *def = g_hash_table_lookup (tracker->service_id_table, str_id);
g_free (str_id);
if (!def) {
return NULL;
}
return g_strdup (def->parent);
}
int
tracker_get_parent_id_for_service_id (int service_type_id)
{
char *str_id = tracker_int_to_str (service_type_id);
ServiceDef *def = g_hash_table_lookup (tracker->service_id_table, str_id);
g_free (str_id);
if (!def) {
return -1;
}
char *name = g_utf8_strdown (def->parent, -1);
ServiceDef *def2 = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
if (!def2) {
return -1;
}
return def2->id;
}
int
tracker_get_id_for_service (const char *service)
{
char *name = g_utf8_strdown (service, -1);
ServiceDef *def = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
if (!def) {
return -1;
}
return def->id;
}
int
tracker_get_id_for_parent_service (const char *service)
{
char *name = g_utf8_strdown (service, -1);
ServiceDef *def = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
if (!def) {
return -1;
}
return tracker_get_id_for_service (def->parent);
}
char *
tracker_get_parent_service (const char *service)
{
char *name = g_utf8_strdown (service, -1);
ServiceDef *def = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
if (!def) {
return NULL;
}
if (def->parent) {
return g_strdup (def->parent);
}
return NULL;
}
ServiceDef *
tracker_get_service (const char *service)
{
char *name = g_utf8_strdown (service, -1);
ServiceDef *def = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
return def;
}
DBTypes
tracker_get_db_for_service (const char *service)
{
char *name = g_utf8_strdown (service, -1);
if (g_str_has_prefix (name, "emails") || g_str_has_prefix (name, "attachments")) {
g_free (name);
return DB_EMAIL;
}
int id = tracker_get_id_for_parent_service (name);
char *str_id = tracker_int_to_str (id);
// ServiceDef *def = g_hash_table_lookup (tracker->service_id_table, str_id);
g_free (str_id);
g_free (name);
// if (!def) {
return DB_DATA;
// return def->database;
}
gboolean
tracker_is_service_embedded (const char *service)
{
char *name = g_utf8_strdown (service, -1);
ServiceDef *def = g_hash_table_lookup (tracker->service_table, name);
g_free (name);
if (!def) {
return FALSE;
}
return def->embedded;
}
char **
tracker_make_array_null_terminated (char **array, int length)
{
char **res = NULL;
int i;
res = g_new (char *, length +1);
for (i = 0; i < length; i++) {
res[i] = array[i];
}
res[length] = NULL;
return res;
}
static gboolean
is_int (const char *in)
{
int i, len;
if (!in) {
return FALSE;
}
len = strlen (in);
if (len < 1) {
return FALSE;
}
for (i = 0; i < len; i++) {
if ( !g_ascii_isdigit (in[i]) ) {
return FALSE;
}
}
return TRUE;
}
static int
parse_month (const char *month)
{
int i;
for (i = 0; i < 12; i++) {
if (!strncmp (month, months[i], 3))
return i;
}
return -1;
}
/* determine date format and convert to ISO 8601 format*/
char *
tracker_format_date (const char *timestamp)
{
char tmp_buf[30];
int len;
if (!timestamp) {
return NULL;
}
len = strlen (timestamp);
/* we cannot format a date without at least a four digit year */
if (len < 4) {
return NULL;
}
/* check for year only dates (EG ID3 music tags might have Audio.ReleaseDate as 4 digit year) */
if (len == 4) {
if (is_int (timestamp)) {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = '0';
tmp_buf[6] = '1';
tmp_buf[7] = '-';
tmp_buf[8] = '0';
tmp_buf[9] = '1';
tmp_buf[10] = 'T';
tmp_buf[11] = '0';
tmp_buf[12] = '0';
tmp_buf[13] = ':';
tmp_buf[14] = '0';
tmp_buf[15] = '0';
tmp_buf[16] = ':';
tmp_buf[17] = '0';
tmp_buf[18] = '0';
tmp_buf[19] = '\0';
return g_strdup (tmp_buf);
} else {
return NULL;
}
/* check for date part only YYYY-MM-DD*/
} else if (len == 10) {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = timestamp[5];
tmp_buf[6] = timestamp[6];
tmp_buf[7] = '-';
tmp_buf[8] = timestamp[8];
tmp_buf[9] = timestamp[9];
tmp_buf[10] = 'T';
tmp_buf[11] = '0';
tmp_buf[12] = '0';
tmp_buf[13] = ':';
tmp_buf[14] = '0';
tmp_buf[15] = '0';
tmp_buf[16] = ':';
tmp_buf[17] = '0';
tmp_buf[18] = '0';
tmp_buf[19] = '\0';
return g_strdup (tmp_buf);
/* check for pdf format EG 20050315113224-08'00' or 20050216111533Z */
} else if (len == 14) {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = timestamp[4];
tmp_buf[6] = timestamp[5];
tmp_buf[7] = '-';
tmp_buf[8] = timestamp[6];
tmp_buf[9] = timestamp[7];
tmp_buf[10] = 'T';
tmp_buf[11] = timestamp[8];
tmp_buf[12] = timestamp[9];
tmp_buf[13] = ':';
tmp_buf[14] = timestamp[10];
tmp_buf[15] = timestamp[11];
tmp_buf[16] = ':';
tmp_buf[17] = timestamp[12];
tmp_buf[18] = timestamp[13];
tmp_buf[19] = '\0';
return g_strdup (tmp_buf);
} else if (len == 15 && timestamp[14] == 'Z') {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = timestamp[4];
tmp_buf[6] = timestamp[5];
tmp_buf[7] = '-';
tmp_buf[8] = timestamp[6];
tmp_buf[9] = timestamp[7];
tmp_buf[10] = 'T';
tmp_buf[11] = timestamp[8];
tmp_buf[12] = timestamp[9];
tmp_buf[13] = ':';
tmp_buf[14] = timestamp[10];
tmp_buf[15] = timestamp[11];
tmp_buf[16] = ':';
tmp_buf[17] = timestamp[12];
tmp_buf[18] = timestamp[13];
tmp_buf[19] = 'Z';
tmp_buf[20] = '\0';
return g_strdup (tmp_buf);
} else if (len == 21 && (timestamp[14] == '-' || timestamp[14] == '+' )) {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = timestamp[4];
tmp_buf[6] = timestamp[5];
tmp_buf[7] = '-';
tmp_buf[8] = timestamp[6];
tmp_buf[9] = timestamp[7];
tmp_buf[10] = 'T';
tmp_buf[11] = timestamp[8];
tmp_buf[12] = timestamp[9];
tmp_buf[13] = ':';
tmp_buf[14] = timestamp[10];
tmp_buf[15] = timestamp[11];
tmp_buf[16] = ':';
tmp_buf[17] = timestamp[12];
tmp_buf[18] = timestamp[13];
tmp_buf[19] = timestamp[14];
tmp_buf[20] = timestamp[15];
tmp_buf[21] = timestamp[16];
tmp_buf[22] = ':';
tmp_buf[23] = timestamp[18];
tmp_buf[24] = timestamp[19];
tmp_buf[25] = '\0';
return g_strdup (tmp_buf);
/* check for msoffice date format "Mon Feb 9 10:10:00 2004" */
} else if ((len == 24) && (timestamp[3] == ' ')) {
int num_month;
char mon1;
char day1;
num_month = parse_month (timestamp + 4);
mon1 = imonths[num_month];
if (timestamp[8] == ' ') {
day1 = '0';
} else {
day1 = timestamp[8];
}
tmp_buf[0] = timestamp[20];
tmp_buf[1] = timestamp[21];
tmp_buf[2] = timestamp[22];
tmp_buf[3] = timestamp[23];
tmp_buf[4] = '-';
if (num_month < 10) {
tmp_buf[5] = '0';
tmp_buf[6] = mon1;
} else {
tmp_buf[5] = '1';
tmp_buf[6] = mon1;
}
tmp_buf[7] = '-';
tmp_buf[8] = day1;
tmp_buf[9] = timestamp[9];
tmp_buf[10] = 'T';
tmp_buf[11] = timestamp[11];
tmp_buf[12] = timestamp[12];
tmp_buf[13] = ':';
tmp_buf[14] = timestamp[14];
tmp_buf[15] = timestamp[15];
tmp_buf[16] = ':';
tmp_buf[17] = timestamp[17];
tmp_buf[18] = timestamp[18];
tmp_buf[19] = '\0';
return g_strdup (tmp_buf);
/* check for Exif date format "2005:04:29 14:56:54" */
} else if ((len == 19) && (timestamp[4] == ':') && (timestamp[7] == ':')) {
tmp_buf[0] = timestamp[0];
tmp_buf[1] = timestamp[1];
tmp_buf[2] = timestamp[2];
tmp_buf[3] = timestamp[3];
tmp_buf[4] = '-';
tmp_buf[5] = timestamp[5];
tmp_buf[6] = timestamp[6];
tmp_buf[7] = '-';
tmp_buf[8] = timestamp[8];
tmp_buf[9] = timestamp[9];
tmp_buf[10] = 'T';
tmp_buf[11] = timestamp[11];
tmp_buf[12] = timestamp[12];
tmp_buf[13] = ':';
tmp_buf[14] = timestamp[14];
tmp_buf[15] = timestamp[15];
tmp_buf[16] = ':';
tmp_buf[17] = timestamp[17];
tmp_buf[18] = timestamp[18];
tmp_buf[19] = '\0';
return g_strdup (tmp_buf);
}
return g_strdup (timestamp);
}
static gboolean
is_valid_8601_datetime (const char *timestamp)
{
int len;
len = strlen (timestamp);
if (len < 19) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[0]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[1]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[2]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[3]) ) {
return FALSE;
}
if (timestamp[4] != '-') {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[5]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[6]) ) {
return FALSE;
}
if (timestamp[7] != '-') {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[8]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[9]) ) {
return FALSE;
}
if ( (timestamp[10] != 'T') ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[11]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[12]) ) {
return FALSE;
}
if (timestamp[13] != ':') {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[14]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[15]) ) {
return FALSE;
}
if (timestamp[16] != ':'){
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[17]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[18]) ){
return FALSE;
}
if (len == 20) {
if (timestamp[19] != 'Z') {
return FALSE;
}
} else {
if (len > 20) {
/* format must be YYYY-MM-DDThh:mm:ss+xx or YYYY-MM-DDThh:mm:ss+xx:yy */
if (len < 22 || len > 25) {
return FALSE;
}
if ( (timestamp[19] != '+') && (timestamp[19] != '-') ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[20]) ) {
return FALSE;
}
if ( !g_ascii_isdigit (timestamp[21]) ) {
return FALSE;
}
}
}
return TRUE;
}
time_t
tracker_str_to_date (const char *timestamp)
{
struct tm tm;
long val;
time_t tt;
g_return_val_if_fail (timestamp, -1);
/* we should have a valid iso 8601 date in format YYYY-MM-DDThh:mm:ss with optional TZ*/
if (!is_valid_8601_datetime (timestamp)) {
return -1;
}
memset (&tm, 0, sizeof (struct tm));
val = strtoul (timestamp, (char **)×tamp, 10);
if (*timestamp == '-') {
// YYYY-MM-DD
tm.tm_year = val - 1900;
timestamp++;
tm.tm_mon = strtoul (timestamp, (char **)×tamp, 10) -1;
if (*timestamp++ != '-') {
return -1;
}
tm.tm_mday = strtoul (timestamp, (char **)×tamp, 10);
}
if (*timestamp++ != 'T') {
tracker_error ("ERROR: date validation failed for %s st %c", timestamp, *timestamp);
return -1;
}
val = strtoul (timestamp, (char **)×tamp, 10);
if (*timestamp == ':') {
// hh:mm:ss
tm.tm_hour = val;
timestamp++;
tm.tm_min = strtoul (timestamp, (char **)×tamp, 10);
if (*timestamp++ != ':') {
return -1;
}
tm.tm_sec = strtoul (timestamp, (char **)×tamp, 10);
}
tt = timegm (&tm);
if (*timestamp == '+' || *timestamp == '-') {
int sign;
sign = (*timestamp++ == '+') ? -1 : 1;
/* we have format hh:mm or hhmm */
/* now, we are reading hours */
if (timestamp[0] && timestamp[1]) {
if (g_ascii_isdigit (timestamp[0]) && g_ascii_isdigit (timestamp[1])) {
gchar buff[3];
buff[0] = timestamp[0];
buff[1] = timestamp[1];
buff[2] = '\0';
val = strtoul (buff, NULL, 10);
tt += sign * (3600 * val);
timestamp += 2;
}
if (*timestamp == ':' || *timestamp == '\'') {
timestamp++;
}
}
/* now, we are reading minutes */
if (timestamp[0] && timestamp[1]) {
if (g_ascii_isdigit (timestamp[0]) && g_ascii_isdigit (timestamp[1])) {
gchar buff[3];
buff[0] = timestamp[0];
buff[1] = timestamp[1];
buff[2] = '\0';
val = strtoul (buff, NULL, 10);
tt += sign * (60 * val);
timestamp += 2;
}
}
} else {
/*
if (*timestamp == 'Z') {
// no need to do anything if already utc
}
*/
}
return tt;
}
char *
tracker_date_to_str (time_t date_time)
{
char buffer[30];
struct tm loctime;
size_t count;
memset (buffer, '\0', sizeof (buffer));
memset (&loctime, 0, sizeof (struct tm));
localtime_r (&date_time, &loctime);
/* output is ISO 8160 format : "YYYY-MM-DDThh:mm:ss+zz:zz" */
count = strftime (buffer, sizeof (buffer), "%FT%T%z", &loctime);
return (count > 0) ? g_strdup (buffer) : NULL;
}
gboolean
tracker_is_empty_string (const char *s)
{
return s == NULL || s[0] == '\0';
}
gchar *
tracker_long_to_str (glong i)
{
return g_strdup_printf ("%ld", i);
}
gchar *
tracker_int_to_str (gint i)
{
return g_strdup_printf ("%d", i);
}
gchar *
tracker_uint_to_str (guint i)
{
return g_strdup_printf ("%u", i);
}
gboolean
tracker_str_to_uint (const char *s, guint *ret)
{
unsigned long int n;
g_return_val_if_fail (s, FALSE);
n = strtoul (s, NULL, 10);
if (n > G_MAXUINT) {
*ret = 0;
return FALSE;
} else {
*ret = (guint) n;
return TRUE;
}
}
int
tracker_str_in_array (const char *str, char **array)
{
int i;
char **st;
i = 0;
for (st = array; *st; st++) {
if (strcasecmp (*st, str) == 0) {
return i;
}
i++;
}
return -1;
}
char *
tracker_get_radix_by_suffix (const char *str, const char *suffix)
{
g_return_val_if_fail (str, NULL);
g_return_val_if_fail (suffix, NULL);
if (g_str_has_suffix (str, suffix)) {
return g_strndup (str, g_strrstr (str, suffix) - str);
} else {
return NULL;
}
}
char *
tracker_escape_metadata (const char *in)
{
if (!in) {
return NULL;
}
GString *gs = g_string_new ("");
for(; *in; in++) {
if (*in == '|') {
g_string_append_c (gs, 30);
} else {
g_string_append_c (gs, *in);
}
}
return g_string_free (gs, FALSE);
}
char *
tracker_unescape_metadata (const char *in)
{
if (!in) {
return NULL;
}
GString *gs = g_string_new ("");
for(; *in; in++) {
if (*in == 30) {
g_string_append_c (gs, '|');
} else {
g_string_append_c (gs, *in);
}
}
return g_string_free (gs, FALSE);
}
void
tracker_remove_dirs (const char *root_dir)
{
GQueue *dirs;
GSList *dirs_to_remove;
dirs = g_queue_new ();
g_queue_push_tail (dirs, g_strdup (root_dir));
dirs_to_remove = NULL;
while (!g_queue_is_empty (dirs)) {
char *dir;
GDir *dirp;
dir = g_queue_pop_head (dirs);
dirs_to_remove = g_slist_prepend (dirs_to_remove, dir);
if ((dirp = g_dir_open (dir, 0, NULL))) {
const char *file;
while ((file = g_dir_read_name (dirp))) {
char *full_filename;
full_filename = g_build_filename (dir, file, NULL);
if (g_file_test (full_filename, G_FILE_TEST_IS_DIR)) {
g_queue_push_tail (dirs, full_filename);
} else {
g_unlink (full_filename);
g_free (full_filename);
}
}
g_dir_close (dirp);
}
}
g_queue_free (dirs);
/* remove directories (now they are empty) */
g_slist_foreach (dirs_to_remove, (GFunc) g_rmdir, NULL);
g_slist_foreach (dirs_to_remove, (GFunc) g_free, NULL);
g_slist_free (dirs_to_remove);
}
char *
tracker_format_search_terms (const char *str, gboolean *do_bool_search)
{
char *def_prefix;
char **terms;
*do_bool_search = FALSE;
def_prefix = "+";
if (strlen (str) < 3) {
return g_strdup (str);
}
/* if already has quotes then do nothing */
if (strchr (str, '"') || strchr (str, '*')) {
*do_bool_search = TRUE;
return g_strdup (str);
}
if (strstr (str, " or ")) {
def_prefix = " ";
}
terms = g_strsplit (str, " ", -1);
if (terms) {
GString *search_term;
char **st;
char *prefix;
search_term = g_string_new (" ");
for (st = terms; *st; st++) {
if (*st[0] == '-') {
prefix = " ";
} else {
prefix = def_prefix;
}
if ((*st[0] != '-') && strchr (*st, '-')) {
char *s;
*do_bool_search = TRUE;
s = g_strconcat ("\"", *st, "\"", NULL);
g_string_append (search_term, s);
g_free (s);
} else {
g_string_append_printf (search_term, " %s%s ", prefix, *st);
}
}
g_strfreev (terms);
return g_string_free (search_term, FALSE);
}
return g_strdup (str);
}
void
tracker_print_object_allocations (void)
{
tracker_log ("Total allocations = %d, total deallocations = %d", info_allocated, info_deallocated);
}
static inline gboolean
is_in_path (const char *uri, const char *path)
{
char *tmp = g_strconcat (path, G_DIR_SEPARATOR_S, NULL);
gboolean result = g_str_has_prefix (uri, tmp);
g_free (tmp);
return result;
}
gboolean
tracker_file_is_no_watched (const char* uri)
{
GSList *lst;
if (!tracker_check_uri (uri)) {
return TRUE;
}
if (is_in_path (uri, g_get_tmp_dir ())) {
return TRUE;
}
if (is_in_path (uri, "/proc")) {
return TRUE;
}
if (is_in_path (uri, "/dev")) {
return TRUE;
}
if (is_in_path (uri, "/tmp")) {
return TRUE;
}
if (!tracker->no_watch_directory_list) {
return FALSE;
}
for (lst = tracker->no_watch_directory_list; lst; lst = lst->next) {
char *compare_uri = lst->data;
/* check if equal or a prefix with an appended '/' */
if (strcmp (uri, compare_uri) == 0) {
tracker_debug ("blocking watch of %s", uri);
return TRUE;
}
if (is_in_path (uri, compare_uri)) {
tracker_debug ("blocking watch of %s", uri);
return TRUE;
}
}
return FALSE;
}
gboolean
tracker_file_is_crawled (const char* uri)
{
GSList *lst;
if (!tracker->crawl_directory_list) {
return FALSE;
}
if (!uri || uri[0] != '/') {
return FALSE;
}
for (lst = tracker->crawl_directory_list; lst; lst = lst->next) {
char *compare_uri = lst->data;
/* check if equal or a prefix with an appended '/' */
if (strcmp (uri, compare_uri) == 0) {
tracker_debug ("blocking watch of %s", uri);
return TRUE;
}
if (is_in_path (uri, compare_uri)) {
tracker_debug ("blocking watch of %s", uri);
return TRUE;
}
}
return FALSE;
}
void
tracker_add_root_dir (const char *uri)
{
struct stat st;
if (! tracker->skip_mount_points) {
return;
}
if (!uri || uri[0] != '/') {
return;
}
if (g_stat (uri, &st) == 0) {
GSList * cur = NULL;
dev_t * elem = NULL;
if (! S_ISDIR (st.st_mode)) {
return;
}
/* FIXME: too costly? */
for (cur = tracker->root_directory_devices; cur; cur = g_slist_next (cur)) {
if (cur->data && *((dev_t *) cur->data) == st.st_dev) {
return;
}
}
elem = g_new (dev_t, 1);
* elem = st.st_dev;
tracker->root_directory_devices = g_slist_prepend (tracker->root_directory_devices, elem);
} else {
tracker_log ("Could not stat `%s'", uri);
}
}
void
tracker_add_root_directories (GSList * uri_list)
{
GSList * cur = NULL;
if (! tracker->skip_mount_points) {
return;
}
for (cur = uri_list; cur; cur = g_slist_next (cur)) {
tracker_add_root_dir ((const char *) cur->data);
}
}
gboolean
tracker_file_is_in_root_dir (const char *uri)
{
struct stat st;
GSList * cur = NULL;
dev_t uri_dev = 0;
if (! tracker->skip_mount_points) {
return TRUE;
}
if (!uri || uri[0] != '/') {
return FALSE;
}
if (g_stat (uri, &st) == 0) {
uri_dev = st.st_dev;
} else {
tracker_log ("Could not stat `%s'", uri);
return TRUE; /* the caller should take care of skipping this one */
}
if (! S_ISDIR (st.st_mode)) { /* only directories are mount points and therefore checked */
return TRUE;
}
for (cur = tracker->root_directory_devices; cur; cur = g_slist_next (cur)) {
if (cur->data && *((dev_t *) cur->data) == uri_dev) {
return TRUE;
}
}
return FALSE;
}
gboolean
tracker_file_info_is_valid (FileInfo *info)
{
if (!info || !info->uri) {
tracker_log ("************** Warning Invalid Info struct detected *****************");
return FALSE;
} else {
if ( !g_utf8_validate (info->uri, -1, NULL) || info->action == TRACKER_ACTION_IGNORE) {
if (info->action != TRACKER_ACTION_IGNORE) {
tracker_log ("************** Warning UTF8 Validation of FileInfo URI has failed (possible corruption) *****************");
}
tracker_free_file_info (info);
return FALSE;
}
}
return TRUE;
}
void
tracker_free_array (char **array, int row_count)
{
if (array && (row_count > 0)) {
int i;
for (i = 0; i < row_count; i++) {
if (array[i]) {
g_free (array[i]);
}
}
g_free (array);
}
}
FileInfo *
tracker_create_file_info (const char *uri, TrackerChangeAction action, int counter, WatchTypes watch)
{
FileInfo *info;
info = g_slice_new0 (FileInfo);
info->action = action;
info->uri = g_strdup (uri);
info->counter = counter;
info->file_id = 0;
info->file_type = FILE_ORDINARY;
info->watch_type = watch;
info->is_directory = FALSE;
info->is_link = FALSE;
info->link_id = 0;
info->link_path = NULL;
info->link_name = NULL;
info->mime = NULL;
info->file_size = 0;
info->permissions = g_strdup ("-r--r--r--");
info->mtime = 0;
info->atime = 0;
info->indextime = 0;
info->offset = 0;
info->aux_id = -1;
info->is_hidden = FALSE;
info->is_new = TRUE;
info->service_type_id = -1;
info->ref_count = 1;
info_allocated ++;
return info;
}
FileInfo *
tracker_free_file_info (FileInfo *info)
{
if (!info) {
return NULL;
}
if (info->uri) {
g_free (info->uri);
}
if (info->link_path) {
g_free (info->link_path);
}
if (info->link_name) {
g_free (info->link_name);
}
if (info->mime) {
g_free (info->mime);
}
if (info->permissions) {
g_free (info->permissions);
}
g_slice_free (FileInfo, info);
info_deallocated ++;
return NULL;
}
/* ref count FileInfo instances */
FileInfo *
tracker_inc_info_ref (FileInfo *info)
{
if (info) {
g_atomic_int_inc (&info->ref_count);
}
return info;
}
FileInfo *
tracker_dec_info_ref (FileInfo *info)
{
if (!info) {
return NULL;
}
if g_atomic_int_dec_and_test (&info->ref_count) {
tracker_free_file_info (info);
return NULL;
}
return info;
}
FileInfo *
tracker_get_pending_file_info (guint32 file_id, const char *uri, const char *mime, int counter, TrackerChangeAction action, gboolean is_directory)
{
FileInfo *info;
info = g_slice_new0 (FileInfo);
info->action = action;
info->uri = g_strdup (uri);
info->counter = counter;
info->file_id = file_id;
info->file_type = FILE_ORDINARY;
info->is_directory = is_directory;
info->is_link = FALSE;
info->link_id = 0;
info->link_path = NULL;
info->link_name = NULL;
if (mime) {
info->mime = g_strdup (mime);
} else {
info->mime = NULL;
}
info->file_size = 0;
info->permissions = g_strdup ("-r--r--r--");
info->mtime = 0;
info->atime = 0;
info->indextime = 0;
info->offset = 0;
info->service_type_id = -1;
info->is_new = TRUE;
info->ref_count = 1;
info_allocated ++;
return info;
}
gint32
tracker_get_file_mtime (const char *uri)
{
struct stat finfo;
char *uri_in_locale;
uri_in_locale = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
if (uri_in_locale) {
if (g_lstat (uri_in_locale, &finfo) == -1) {
g_free (uri_in_locale);
return 0;
}
} else {
tracker_error ("ERROR: uri could not be converted to locale format");
return 0;
}
g_free (uri_in_locale);
return (gint32) finfo.st_mtime;
}
FileInfo *
tracker_get_file_info (FileInfo *info)
{
struct stat finfo;
char *str, *uri_in_locale;
if (!info || !info->uri) {
return info;
}
uri_in_locale = g_filename_from_utf8 (info->uri, -1, NULL, NULL, NULL);
if (uri_in_locale) {
if (g_lstat (uri_in_locale, &finfo) == -1) {
g_free (uri_in_locale);
return info;
}
} else {
tracker_error ("ERROR: info->uri could not be converted to locale format");
return NULL;
}
info->is_directory = S_ISDIR (finfo.st_mode);
info->is_link = S_ISLNK (finfo.st_mode);
if (info->is_link && !info->link_name) {
str = g_file_read_link (uri_in_locale, NULL);
if (str) {
char *link_uri;
link_uri = g_filename_to_utf8 (str, -1, NULL, NULL, NULL);
info->link_name = g_path_get_basename (link_uri);
info->link_path = g_path_get_dirname (link_uri);
g_free (link_uri);
g_free (str);
}
}
g_free (uri_in_locale);
if (!info->is_directory) {
info->file_size = (guint32) finfo.st_size;
} else {
if (info->watch_type == WATCH_OTHER) {
info->watch_type = WATCH_SUBFOLDER;
}
}
g_free (info->permissions);
info->permissions = tracker_create_permission_string (finfo);
info->mtime = finfo.st_mtime;
info->atime = finfo.st_atime;
return info;
}
GSList *
tracker_get_service_dirs (const char *service)
{
GSList *list = NULL, *tmp;
if (!service) {
return NULL;
}
for (tmp = tracker->service_directory_list; tmp; tmp = tmp->next) {
char *path = (char *) tmp->data;
char *path_service = g_hash_table_lookup (tracker->service_directory_table, path);
if (strcasecmp (service, path_service) ==0) {
list = g_slist_prepend (list, path);
}
}
return list;
}
void
tracker_add_service_path (const char *service, const char *path)
{
if (!service || !path || !tracker_file_is_valid (path)) {
return;
}
char *dir_path = g_strdup (path);
char *service_type = g_strdup (service);
tracker->service_directory_list = g_slist_prepend (tracker->service_directory_list, dir_path);
g_hash_table_insert (tracker->service_directory_table, dir_path, service_type);
}
char *
tracker_get_service_for_uri (const char *uri)
{
GSList *tmp;
/* check service dir list to see if a prefix */
for (tmp = tracker->service_directory_list; tmp; tmp = tmp->next) {
char *prefix;
prefix = (char *) tmp->data;
if (prefix && g_str_has_prefix (uri, prefix)) {
return g_strdup (g_hash_table_lookup (tracker->service_directory_table, prefix));
}
}
return g_strdup ("Files");
}
gboolean
tracker_is_service_file (const char *uri)
{
char *service;
gboolean result;
service = tracker_get_service_for_uri (uri);
result = (service && strcmp (service, "Files") != 0);
g_free (service);
return result;
}
gboolean
tracker_file_is_valid (const char *uri)
{
gboolean convert_ok;
char *uri_in_locale;
uri_in_locale = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
if (!uri_in_locale) {
tracker_error ("ERROR: uri could not be converted to locale format");
g_free (uri_in_locale);
return FALSE;
}
/* g_file_test(file,G_FILE_TEST_EXISTS) uses the access() system call and so needs locale filenames. */
convert_ok = g_file_test (uri_in_locale, G_FILE_TEST_EXISTS);
g_free (uri_in_locale);
return convert_ok;
}
gboolean
tracker_file_is_indexable (const char *uri)
{
char *uri_in_locale;
struct stat finfo;
gboolean convert_ok;
uri_in_locale = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
if (!uri_in_locale) {
tracker_error ("ERROR: uri could not be converted to locale format");
return FALSE;
}
g_lstat (uri_in_locale, &finfo);
g_free (uri_in_locale);
convert_ok = (!S_ISDIR (finfo.st_mode) && S_ISREG (finfo.st_mode));
if (convert_ok) {
tracker_debug ("file %s is indexable", uri);
} else {
tracker_debug ("file %s is *not* indexable", uri);
}
return convert_ok;
}
static inline gboolean
is_utf8 (const gchar *buffer, gint buffer_length)
{
gchar *end;
/* code in this function modified from gnome-vfs */
if (g_utf8_validate ((gchar *)buffer, buffer_length, (const gchar**)&end)) {
return TRUE;
} else {
/* Check whether the string was truncated in the middle of
* a valid UTF8 char, or if we really have an invalid
* UTF8 string
*/
gint remaining_bytes = buffer_length;
remaining_bytes -= (end-((gchar *)buffer));
if (remaining_bytes > 4) {
return FALSE;
}
if (g_utf8_get_char_validated (end, (gsize) remaining_bytes) == (gunichar) -2) {
return TRUE;
}
}
return FALSE;
}
static gboolean
is_text_file (const gchar *uri)
{
char buffer[TEXT_SNIFF_SIZE];
int buffer_length = 0;
GError *err = NULL;
int fd;
#if defined(__linux__)
fd = open (uri, O_RDONLY|O_NOATIME);
#else
fd = open (uri, O_RDONLY);
#endif
if (fd ==-1) {
return FALSE;
}
buffer_length = read (fd, buffer, TEXT_SNIFF_SIZE);
close (fd);
if (buffer_length < 3) {
return FALSE;
}
/* Don't allow embedded zeros in textfiles. */
if (memchr (buffer, 0, buffer_length) != NULL) {
return FALSE;
}
if (is_utf8 (buffer, buffer_length)) {
return TRUE;
} else {
gchar *tmp = g_locale_to_utf8 (buffer, buffer_length, NULL, NULL, &err);
g_free (tmp);
if (err) {
gboolean result = FALSE;
if (err->code != G_CONVERT_ERROR_ILLEGAL_SEQUENCE && err->code != G_CONVERT_ERROR_FAILED && err->code != G_CONVERT_ERROR_NO_CONVERSION) {
result = TRUE;
}
g_error_free (err);
return result;
}
}
return FALSE;
}
char *
tracker_get_mime_type (const char *uri)
{
struct stat finfo;
char *uri_in_locale;
const char *result;
char *mime;
if (!tracker_file_is_valid (uri)) {
tracker_log ("WARNING: file %s is no longer valid", uri);
return g_strdup ("unknown");
}
uri_in_locale = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
if (!uri_in_locale) {
tracker_error ("ERROR: uri could not be converted to locale format");
return g_strdup ("unknown");
}
g_lstat (uri_in_locale, &finfo);
if (S_ISLNK (finfo.st_mode) && S_ISDIR (finfo.st_mode)) {
g_free (uri_in_locale);
return g_strdup ("symlink");
}
result = xdg_mime_get_mime_type_for_file (uri, NULL);
if (!result || (result == XDG_MIME_TYPE_UNKNOWN)) {
if (is_text_file (uri_in_locale)) {
mime = g_strdup ("text/plain");
} else {
mime = g_strdup ("unknown");
}
} else {
mime = g_strdup (result);
}
g_free (uri_in_locale);
return mime;
}
char *
tracker_get_vfs_path (const char* uri)
{
if (uri != NULL && strchr (uri, G_DIR_SEPARATOR) != NULL) {
char *p;
int len;
len = strlen (uri);
p = (char *) (uri + (len - 1));
/* Skip trailing slash */
if (p != uri && *p == G_DIR_SEPARATOR) {
p--;
}
/* Search backwards to the next slash. */
while (p != uri && *p != G_DIR_SEPARATOR) {
p--;
}
if (p[0] != '\0') {
char *new_uri_text;
int length;
length = p - uri;
if (length == 0) {
new_uri_text = g_strdup (G_DIR_SEPARATOR_S);
} else {
new_uri_text = g_malloc (length + 1);
memcpy (new_uri_text, uri, length);
new_uri_text[length] = '\0';
}
return new_uri_text;
} else {
return g_strdup (G_DIR_SEPARATOR_S);
}
}
return NULL;
}
char *
tracker_get_vfs_name (const char* uri)
{
if (uri != NULL && strchr (uri, G_DIR_SEPARATOR) != NULL) {
char *p, *res, *tmp, *result;
int len;
len = strlen (uri);
tmp = g_strdup (uri);
p = (tmp + (len - 1));
/* Skip trailing slash */
if (p != tmp && *p == G_DIR_SEPARATOR) {
*p = '\0';
}
/* Search backwards to the next slash. */
while (p != tmp && *p != G_DIR_SEPARATOR) {
p--;
}
res = p+1;
if (res && res[0] != '\0') {
result = g_strdup (res);
g_free (tmp);
return result;
}
g_free (tmp);
}
return g_strdup (" ");
}
gboolean
tracker_is_directory (const char *dir)
{
char *dir_in_locale;
dir_in_locale = g_filename_from_utf8 (dir, -1, NULL, NULL, NULL);
if (dir_in_locale) {
struct stat finfo;
g_lstat (dir_in_locale, &finfo);
g_free (dir_in_locale);
return S_ISDIR (finfo.st_mode);
} else {
tracker_error ("ERROR: dir could not be converted to locale format");
}
return FALSE;
}
/*
static int
has_prefix (const char *str1, const char *str2)
{
if (strcmp (str1, str2) == 0) {
return 0;
} else {
char *compare_str;
compare_str = g_strconcat (str1, G_DIR_SEPARATOR_S, NULL);
if (g_str_has_prefix (str2, compare_str)) {
return 0;
}
g_free (compare_str);
return 1;
}
}
*/
static GSList *
get_files (const char *dir, gboolean dir_only, gboolean skip_ignored_files)
{
GDir *dirp;
GSList *file_list;
char *dir_in_locale;
dir_in_locale = g_filename_from_utf8 (dir, -1, NULL, NULL, NULL);
if (!dir_in_locale) {
tracker_error ("ERROR: dir could not be converted to utf8 format");
g_free (dir_in_locale);
return NULL;
}
file_list = NULL;
if ((dirp = g_dir_open (dir_in_locale, 0, NULL))) {
const char *name;
while ((name = g_dir_read_name (dirp))) {
char *mystr, *str;
if (!tracker->is_running) {
g_free (dir_in_locale);
g_dir_close (dirp);
return NULL;
}
str = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
if (!str) {
continue;
}
if (skip_ignored_files && tracker_ignore_file (str)) {
g_free (str);
continue;
}
mystr = g_build_filename (dir, str, NULL);
g_free (str);
if (!tracker_file_is_valid (mystr)) {
g_free (mystr);
continue;
}
if (!tracker_file_is_in_root_dir (mystr)) {
tracker_log ("Skipping mount point %s", mystr);
g_free (mystr);
continue;
}
if (!dir_only || tracker_is_directory (mystr)) {
if (!tracker_file_is_no_watched (mystr)) {
file_list = g_slist_prepend (file_list, g_strdup (mystr));
}
}
g_free (mystr);
}
g_dir_close (dirp);
}
g_free (dir_in_locale);
if (!tracker->is_running) {
if (file_list) {
g_slist_foreach (file_list, (GFunc) g_free, NULL);
g_slist_free (file_list);
}
return NULL;
}
return file_list;
}
GSList *
tracker_get_all_files (const char *dir, gboolean dir_only)
{
return get_files (dir, dir_only, FALSE);
}
GSList *
tracker_get_files (const char *dir, gboolean dir_only)
{
return get_files (dir, dir_only, TRUE);
}
static void
get_dirs (const char *dir, GSList **file_list, gboolean skip_ignored_files)
{
GSList *tmp_list;
g_return_if_fail (dir);
tmp_list = (skip_ignored_files ?
tracker_get_files (dir, TRUE) :
tracker_get_all_files (dir, TRUE));
if (tmp_list) { /* check list is not empty */
if (*file_list) { /* check this list too */
*file_list = g_slist_concat (*file_list, tmp_list);
} else {
*file_list = tmp_list;
}
}
}
void
tracker_get_dirs (const char *dir, GSList **file_list)
{
get_dirs (dir, file_list, TRUE);
}
void
tracker_get_all_dirs (const char *dir, GSList **file_list)
{
get_dirs (dir, file_list, FALSE);
}
static GSList *
check_dir_name (GSList* list, char *input_name)
{
tracker_debug ("checking dir names : %s", input_name);
int is_converted = FALSE;
if (input_name[0] == '~') {
const char* home_dir = g_get_home_dir ();
if ((home_dir != NULL) && (home_dir[0] != '\0')) {
int is_separator = FALSE;
char *new_name;
if (input_name[1] == G_DIR_SEPARATOR) {
is_separator = TRUE;
} else {
if (home_dir[strlen (home_dir)-1] == G_DIR_SEPARATOR) {
is_separator = TRUE;
}
}
new_name = g_strdup_printf ("%s%s%s", home_dir, (is_separator? "" : G_DIR_SEPARATOR_S), &input_name[1]);
if ((new_name != NULL) && (strlen (new_name) <= PATH_MAX)) {
char resolved_name[PATH_MAX+2];
if (realpath (new_name, resolved_name) != NULL) {
list = g_slist_prepend (list, g_strdup (resolved_name));
} else {
list = g_slist_prepend (list, g_strdup (new_name));
}
is_converted = TRUE;
}
if (new_name != NULL) {
g_free (new_name);
}
}
}
if (!is_converted) {
char resolved_name[PATH_MAX+2];
if( realpath (input_name, resolved_name) != NULL ) {
list = g_slist_prepend (list, g_strdup (resolved_name));
} else {
list = g_slist_prepend (list, g_strdup (input_name));
}
}
tracker_debug ("resolved to %s\n", list->data);
return list;
}
GSList *
tracker_filename_array_to_list (char **array)
{
GSList *list;
int i;
list = NULL;
for (i = 0; array[i] != NULL; i++) {
if (!tracker_is_empty_string (array[i])) {
list = check_dir_name (list, array[i]);
}
}
g_strfreev (array);
return list;
}
static GSList *
array_to_list (char **array)
{
GSList *list;
int i;
list = NULL;
for (i = 0; array[i] != NULL; i++) {
if (!tracker_is_empty_string (array[i])) {
list = g_slist_prepend (list, g_strdup (array[i]));
}
}
g_strfreev (array);
return list;
}
GSList *
tracker_array_to_list (char **array)
{
return array_to_list (array);
}
char **
tracker_list_to_array (GSList *list)
{
GSList *tmp;
char **array;
int i;
array = g_new0 (char *, g_slist_length (list) + 1);
i = 0;
for (tmp = list; tmp; tmp = tmp->next) {
if (tmp->data) {
array[i] = g_strdup (tmp->data);
i++;
}
}
array[i] = NULL;
return array;
}
void
tracker_free_strs_in_array (char **array)
{
char **a;
for (a = array; *a; a++) {
g_free (*a);
}
}
gboolean
tracker_ignore_file (const char *uri)
{
char *name;
char **st;
if (tracker_is_empty_string (uri)) {
return TRUE;
}
name = g_path_get_basename (uri);
if (!name || name[0] == '.') {
g_free (name);
return TRUE;
}
/* test suffixes */
for (st = ignore_suffix; *st; st++) {
if (g_str_has_suffix (name, *st)) {
g_free (name);
return TRUE;
}
}
/* test prefixes */
for (st = ignore_prefix; *st; st++) {
if (g_str_has_prefix (name, *st)) {
g_free (name);
return TRUE;
}
}
/* test exact names */
for (st = ignore_name; *st; st++) {
if (strcmp (name, *st) == 0) {
g_free (name);
return TRUE;
}
}
g_free (name);
return FALSE;
}
char *
tracker_array_to_str (char **array, int length, char sep)
{
GString *gstr = g_string_new ("");
int i;
for (i=0; i<length; i++) {
if (array[i]) {
if (i > 0) g_string_append_c (gstr, sep);
gstr = g_string_append (gstr, array[i]);
} else {
break;
}
}
return g_string_free (gstr, FALSE);
}
char *
tracker_get_english_lang_code (void)
{
return g_strdup (tmap[2].lang);
}
gboolean
tracker_is_supported_lang (const char *lang)
{
int i;
for (i = 0; tmap[i].lang; i++) {
if (g_str_has_prefix (lang, tmap[i].lang)) {
return TRUE;
}
}
return FALSE;
}
static char *
get_default_language_code (void)
{
char **langs, **plangs;
/* get langauges for user's locale */
langs = (char**) g_get_language_names ();
for (plangs = langs; *plangs; plangs++) {
if (strlen (*plangs) > 1) {
int i;
for (i = 0; tmap[i].lang; i++) {
if (g_str_has_prefix (*plangs, tmap[i].lang)) {
return g_strndup (*plangs, 2);
}
}
}
}
return g_strdup ("en");
}
void
tracker_set_language (const char *language, gboolean create_stemmer)
{
if (!language || strlen (language) < 2) {
if (tracker->language) {
g_free (tracker->language);
}
tracker->language = get_default_language_code ();
tracker_log ("Setting default language code to %s based on user's locale", language);
} else {
int i;
for (i = 0; tmap[i].lang; i++) {
if (g_str_has_prefix (language, tmap[i].lang)) {
if (tracker->language) {
g_free (tracker->language);
}
tracker->language = g_strdup (tmap[i].lang);
break;
}
}
}
/* set stopwords list and create stemmer for language */
tracker_log ("setting stopword list for language code %s", language);
char *stopword_path, *stopword_file, *stopword_en_file = NULL;
char *stopwords = NULL;
stopword_path = g_build_filename (TRACKER_DATADIR, "tracker", "languages", "stopwords", NULL);
stopword_file = g_strconcat (stopword_path, ".", language, NULL);
if (strcmp (language, "en") != 0) {
stopword_en_file = g_strconcat (stopword_path, ".en", NULL);
}
if (!g_file_get_contents (stopword_file, &stopwords, NULL, NULL)) {
tracker_log ("Warning : Tracker cannot read stopword file %s", stopword_file);
} else {
tracker->stop_words = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
char **words = g_strsplit_set (stopwords, "\n" , -1);
char **pwords;
for (pwords = words; *pwords; pwords++) {
g_hash_table_insert (tracker->stop_words, g_strdup (g_strstrip (*pwords)), GINT_TO_POINTER (1));
}
g_strfreev (words);
}
if (stopword_en_file) {
g_free (stopwords);
if (!g_file_get_contents (stopword_en_file, &stopwords, NULL, NULL)) {
tracker_log ("Warning : Tracker cannot read stopword file %s", stopword_file);
} else {
tracker->stop_words = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
char **words = g_strsplit_set (stopwords, "\n" , -1);
char **pwords;
for (pwords = words; *pwords; pwords++) {
g_hash_table_insert (tracker->stop_words, g_strdup (g_strstrip (*pwords)), GINT_TO_POINTER (1));
}
g_strfreev (words);
}
g_free (stopword_en_file);
}
g_free (stopword_path);
g_free (stopwords);
g_free (stopword_file);
if (!tracker->use_stemmer || !create_stemmer) {
return;
}
char *stem_language;
/* set default language */
stem_language = "english";
if (language) {
int i;
for (i = 0; tmap[i].lang; i++) {
if ((strcasecmp (tmap[i].lang, language) == 0)) {
stem_language = tmap[i].name;
break;
}
}
}
tracker->stemmer = sb_stemmer_new (stem_language, NULL);
if (!tracker->stemmer) {
tracker_log ("Warning : No stemmer could be found for language %s", stem_language);
} else {
tracker_log ("Using stemmer for language %s\n", stem_language);
}
}
void
tracker_load_config_file (void)
{
GKeyFile *key_file;
char *filename;
char **values;
key_file = g_key_file_new ();
filename = g_build_filename (tracker->config_dir, "/tracker/tracker.cfg", NULL);
if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
char *tracker_dir = g_build_filename (tracker->config_dir,"/tracker",NULL);
if (!g_file_test (tracker_dir,G_FILE_TEST_EXISTS)) {
g_mkdir_with_parents (tracker_dir,0700);
}
g_free (tracker_dir);
char *contents, *language;
language = get_default_language_code ();
contents = g_strconcat (
"[General]\n",
"# Log Verbosity - Valid values are 0 (displays/logs only errors), 1 (minimal), 2 (detailed), and 3 (debug)\n",
"Verbosity=0\n\n",
"# Set the initial sleeping time, in seconds\n",
"InitialSleep=45\n\n",
"# Minimizes the use of memory but may slow indexing down\n",
"LowMemoryMode=false\n\n",
"[Watches]\n",
"# List of directory roots to index and watch seperated by semicolons\n",
"WatchDirectoryRoots=", g_get_home_dir (), ";\n",
"# List of directory roots to index but not watch (no live updates but are refreshed when trackerd is next restarted) seperated by semicolons\n",
"CrawlDirectory=\n",
"# List of directory roots to not index and not watch seperated by semicolons\n",
"NoWatchDirectory=\n",
"# Set to false to prevent watching of any kind\n",
"EnableWatching=true\n\n",
"[Indexing]\n",
"# Throttles the indexing process. Allowable values are 0-20. higher values decrease indexing speed\n",
"Throttle=0\n",
"# Disables the indexing process\n",
"EnableIndexing=true\n",
"# Enables indexing of a file's text contents\n",
"EnableFileContentIndexing=true\n",
"# Enables generation of thumbnails\n",
"EnableThumbnails=false\n",
"# List of partial file patterns (glob) seperated by semicolons that specify files to not index (basic stat info is only indexed for files that match these patterns)\n",
"NoIndexFileTypes=;\n\n",
"# Sets minimum length of words to index\n",
"MinWordLength=3\n",
"# Sets maximum length of words to index (words are cropped if bigger than this)\n",
"MaxWordLength=30\n",
"# Sets the language specific stemmer and stopword list to use \n",
"# Valid values are 'en' (english), 'da' (danish), 'nl' (dutch), 'fi' (finnish), 'fr' (french), 'de' (german), 'it' (italien), 'nb' (norwegian), 'pt' (portugese), 'ru' (russian), 'es' (spanish), 'sv' (swedish)\n",
"Language=", language, "\n",
"# Enables use of language-specific stemmer\n",
"EnableStemmer=true\n",
"# Set to true prevents tracker from descending into mounted directory trees\n",
"SkipMountPoints=false\n\n",
"[Emails]\n",
"IndexEvolutionEmails=true\n",
"[Performance]\n",
"# Maximum size of text in bytes to index from a file's text contents\n",
"MaxTextToIndex=1048576\n",
"# Maximum number of unique words to index from a file's text contents\n",
"MaxWordsToIndex=10000\n",
"# Specifies the no of entities to index before determining whether to perform index optimization\n",
"OptimizationSweepCount=10000\n",
"# Sets the maximum bucket count for the indexer\n",
"MaxBucketCount=524288\n",
"# Sets the minimum bucket count\n",
"MinBucketCount=65536\n",
"# Sets no. of divisions of the index file\n",
"Dvisions=4\n",
"# Selects the desired ratio of used records to buckets to be used when optimizing index (should be a value between 0 and 4) \n",
"BucketRatio=1\n",
"# Alters how much padding is used to prevent index relocations. Higher values improve indexing speed but waste more disk space. Value should be in range (1..8)\n",
"Padding=2\n",
NULL);
g_file_set_contents (filename, contents, strlen (contents), NULL);
g_free (contents);
}
/* load all options into tracker struct */
g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
/* general options */
if (g_key_file_has_key (key_file, "General", "Verbosity", NULL)) {
tracker->verbosity = g_key_file_get_integer (key_file, "General", "Verbosity", NULL);
}
if (g_key_file_has_key (key_file, "General", "LowMemoryMode", NULL)) {
tracker->use_extra_memory = !g_key_file_get_boolean (key_file, "General", "LowMemoryMode", NULL);
}
if (g_key_file_has_key (key_file, "General", "InitialSleep", NULL)) {
tracker->initial_sleep = g_key_file_get_integer (key_file, "General", "InitialSleep", NULL);
}
/* Watch options */
values = g_key_file_get_string_list (key_file,
"Watches",
"WatchDirectoryRoots",
NULL,
NULL);
if (values) {
tracker->watch_directory_roots_list = tracker_filename_array_to_list (values);
} else {
tracker->watch_directory_roots_list = g_slist_prepend (tracker->watch_directory_roots_list, g_strdup (g_get_home_dir ()));
}
values = g_key_file_get_string_list (key_file,
"Watches",
"CrawlDirectory",
NULL,
NULL);
if (values) {
tracker->crawl_directory_list = tracker_filename_array_to_list (values);
} else {
tracker->crawl_directory_list = NULL;
}
values = g_key_file_get_string_list (key_file,
"Watches",
"NoWatchDirectory",
NULL,
NULL);
if (values) {
tracker->no_watch_directory_list = tracker_filename_array_to_list (values);
} else {
tracker->no_watch_directory_list = NULL;
}
if (g_key_file_has_key (key_file, "Watches", "EnableWatching", NULL)) {
tracker->enable_watching = g_key_file_get_boolean (key_file, "Watches", "EnableWatching", NULL);
}
/* Indexing options */
if (g_key_file_has_key (key_file, "Indexing", "Throttle", NULL)) {
tracker->throttle = g_key_file_get_integer (key_file, "Indexing", "Throttle", NULL);
} else {
tracker->throttle = 0;
}
if (g_key_file_has_key (key_file, "Indexing", "BatteryThrottle", NULL)) {
tracker->throttle = g_key_file_get_integer (key_file, "Indexing", "BatteryThrottle", NULL);
} else {
tracker->battery_throttle = 10;
}
if (g_key_file_has_key (key_file, "Indexing", "EnableIndexing", NULL)) {
tracker->enable_indexing = g_key_file_get_boolean (key_file, "Indexing", "EnableIndexing", NULL);
} else {
tracker->enable_indexing = TRUE;
}
if (g_key_file_has_key (key_file, "Indexing", "EnableFileContentIndexing", NULL)) {
tracker->enable_content_indexing = g_key_file_get_boolean (key_file, "Indexing", "EnableFileContentIndexing", NULL);
} else {
tracker->enable_content_indexing = TRUE;
}
if (g_key_file_has_key (key_file, "Indexing", "EnableThumbnails", NULL)) {
tracker->enable_thumbnails = g_key_file_get_boolean (key_file, "Indexing", "EnableThumbnails", NULL);
} else {
tracker->enable_thumbnails = FALSE;
}
values = g_key_file_get_string_list (key_file,
"Indexing",
"NoIndexFileTypes",
NULL,
NULL);
if (values) {
tracker->no_index_file_types = array_to_list (values);
} else {
tracker->no_index_file_types = NULL;
}
if (g_key_file_has_key (key_file, "Indexing", "MinWordLength", NULL)) {
tracker->min_word_length = g_key_file_get_integer (key_file, "Indexing", "MinWordLength", NULL);
} else {
tracker->min_word_length = 3;
}
if (g_key_file_has_key (key_file, "Indexing", "MaxWordLength", NULL)) {
tracker->max_word_length = g_key_file_get_integer (key_file, "Indexing", "MaxWordLength", NULL);
} else {
tracker->max_word_length = 40;
}
if (g_key_file_has_key (key_file, "Indexing", "EnableStemmer", NULL)) {
tracker->use_stemmer = g_key_file_get_boolean (key_file, "Indexing", "EnableStemmer", NULL);
} else {
tracker->use_stemmer = TRUE;
}
if (g_key_file_has_key (key_file, "Indexing", "Language", NULL)) {
tracker->language = g_key_file_get_string (key_file, "Indexing", "Language", NULL);
}
if (g_key_file_has_key (key_file, "Indexing", "SkipMountPoints", NULL)) {
tracker->skip_mount_points = g_key_file_get_boolean (key_file, "Indexing", "SkipMountPoints", NULL);
}
/* Emails config */
tracker->additional_mboxes_to_index = NULL;
if (g_key_file_has_key (key_file, "Emails", "IndexEvolutionEmails", NULL)) {
tracker->index_evolution_emails = g_key_file_get_boolean (key_file, "Emails", "IndexEvolutionEmails", NULL);
} else {
tracker->index_evolution_emails = TRUE;
}
if (g_key_file_has_key (key_file, "Emails", "IndexKMailEmails", NULL)) {
tracker->index_kmail_emails = g_key_file_get_boolean (key_file, "Emails", "IndexKMailEmails", NULL);
} else {
tracker->index_kmail_emails = FALSE;
}
/* Performance options */
if (g_key_file_has_key (key_file, "Performance", "MaxTextToIndex", NULL)) {
tracker->max_index_text_length = g_key_file_get_integer (key_file, "Performance", "MaxTextToIndex", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "OptimizationSweepCount", NULL)) {
tracker->optimization_count = g_key_file_get_integer (key_file, "Performance", "OptimizationSweepCount", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "MaxBucketCount", NULL)) {
tracker->max_index_bucket_count = g_key_file_get_integer (key_file, "Performance", "MaxBucketCount", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "MinBucketCount", NULL)) {
tracker->min_index_bucket_count = g_key_file_get_integer (key_file, "Performance", "MinBucketCount", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "Dvisions", NULL)) {
tracker->index_divisions = g_key_file_get_integer (key_file, "Performance", "Dvisions", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "BucketRatio", NULL)) {
tracker->index_bucket_ratio = g_key_file_get_integer (key_file, "Performance", "BucketRatio", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "Padding", NULL)) {
tracker->padding = g_key_file_get_integer (key_file, "Performance", "Padding", NULL);
}
if (g_key_file_has_key (key_file, "Performance", "MaxWordsToIndex", NULL)) {
tracker->max_words_to_index = g_key_file_get_integer (key_file, "Performance", "MaxWordsToIndex", NULL);
}
g_free (filename);
g_key_file_free (key_file);
}
void
tracker_throttle (int multiplier)
{
if (tracker->throttle == 0) {
return;
}
int throttle;
throttle = tracker->throttle * multiplier;
if (throttle > 0) {
g_usleep (throttle);
}
}
void
tracker_notify_file_data_available (void)
{
if (!tracker->is_running) {
return;
}
/* if file thread is asleep then we just need to wake it up! */
if (g_mutex_trylock (tracker->files_signal_mutex)) {
g_cond_signal (tracker->file_thread_signal);
g_mutex_unlock (tracker->files_signal_mutex);
return;
}
/* if busy - check if async queue has new stuff as we do not need to notify then */
if (g_async_queue_length (tracker->file_process_queue) > 1) {
return;
}
/* if file thread not in check phase then we need do nothing */
if (g_mutex_trylock (tracker->files_check_mutex)) {
g_mutex_unlock (tracker->files_check_mutex);
return;
}
/* we are in check phase - we need to wait until either check_mutex is unlocked or file thread is asleep then awaken it */
while (TRUE) {
if (g_mutex_trylock (tracker->files_check_mutex)) {
g_mutex_unlock (tracker->files_check_mutex);
return;
}
if (g_mutex_trylock (tracker->files_signal_mutex)) {
g_cond_signal (tracker->file_thread_signal);
g_mutex_unlock (tracker->files_signal_mutex);
return;
}
g_thread_yield ();
g_usleep (10);
}
}
void
tracker_notify_meta_data_available (void)
{
if (!tracker->is_running) {
return;
}
/* if metadata thread is asleep then we just need to wake it up! */
if (g_mutex_trylock (tracker->metadata_signal_mutex)) {
g_cond_signal (tracker->metadata_thread_signal);
g_mutex_unlock (tracker->metadata_signal_mutex);
return;
}
/* if busy - check if async queue has new stuff as we do not need to notify then */
if (g_async_queue_length (tracker->file_metadata_queue) > 1) {
return;
}
/* if metadata thread not in check phase then we need do nothing */
if (g_mutex_trylock (tracker->metadata_check_mutex)) {
g_mutex_unlock (tracker->metadata_check_mutex);
return;
}
/* we are in check phase - we need to wait until either check_mutex is unlocked or until metadata thread is asleep then we awaken it */
while (TRUE) {
if (g_mutex_trylock (tracker->metadata_check_mutex)) {
g_mutex_unlock (tracker->metadata_check_mutex);
return;
}
if (g_mutex_trylock (tracker->metadata_signal_mutex)) {
g_cond_signal (tracker->metadata_thread_signal);
g_mutex_unlock (tracker->metadata_signal_mutex);
return;
}
g_thread_yield ();
g_usleep (10);
tracker_debug ("in check phase");
}
}
void
tracker_notify_request_data_available (void)
{
/* if thread is asleep then we just need to wake it up! */
if (g_mutex_trylock (tracker->request_signal_mutex)) {
g_cond_signal (tracker->request_thread_signal);
g_mutex_unlock (tracker->request_signal_mutex);
return;
}
/* if thread not in check phase then we need do nothing */
if (g_mutex_trylock (tracker->request_check_mutex)) {
g_mutex_unlock (tracker->request_check_mutex);
return;
}
/* we are in check phase - we need to wait until either check_mutex is unlocked or thread is asleep then awaken it */
while (TRUE) {
if (g_mutex_trylock (tracker->request_check_mutex)) {
g_mutex_unlock (tracker->request_check_mutex);
return;
}
if (g_mutex_trylock (tracker->request_signal_mutex)) {
g_cond_signal (tracker->request_thread_signal);
g_mutex_unlock (tracker->request_signal_mutex);
return;
}
g_thread_yield ();
g_usleep (10);
}
}
GTimeVal *
tracker_timer_start (void)
{
GTimeVal *before;
before = g_new0 (GTimeVal, 1);
g_get_current_time (before);
return before;
}
void
tracker_timer_end (GTimeVal *before, const char *str)
{
GTimeVal after;
double elapsed;
g_get_current_time (&after);
elapsed = (1000 * (after.tv_sec - before->tv_sec)) + ((after.tv_usec - before->tv_usec) / 1000.0);
g_free (before);
tracker_log ("%s %f ms", str, elapsed);
}
char *
tracker_compress (const char *ptr, int size, int *compressed_size)
{
z_stream zs;
char *buf, *swap;
unsigned char obuf[ZLIBBUFSIZ];
int rv, asiz, bsiz, osiz;
if (size < 0) size = strlen (ptr);
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (deflateInit2 (&zs, 6, Z_DEFLATED, 15, 6, Z_DEFAULT_STRATEGY) != Z_OK) {
return NULL;
}
asiz = size + 16;
if (asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
if (!(buf = malloc (asiz))) {
deflateEnd (&zs);
return NULL;
}
bsiz = 0;
zs.next_in = (unsigned char *)ptr;
zs.avail_in = size;
zs.next_out = obuf;
zs.avail_out = ZLIBBUFSIZ;
while ( (rv = deflate (&zs, Z_FINISH)) == Z_OK) {
osiz = ZLIBBUFSIZ - zs.avail_out;
if (bsiz + osiz > asiz) {
asiz = asiz * 2 + osiz;
if (!(swap = realloc (buf, asiz))) {
free (buf);
deflateEnd (&zs);
return NULL;
}
buf = swap;
}
memcpy (buf + bsiz, obuf, osiz);
bsiz += osiz;
zs.next_out = obuf;
zs.avail_out = ZLIBBUFSIZ;
}
if (rv != Z_STREAM_END) {
free (buf);
deflateEnd (&zs);
return NULL;
}
osiz = ZLIBBUFSIZ - zs.avail_out;
if (bsiz + osiz + 1 > asiz) {
asiz = asiz * 2 + osiz;
if (!(swap = realloc (buf, asiz))) {
free (buf);
deflateEnd (&zs);
return NULL;
}
buf = swap;
}
memcpy (buf + bsiz, obuf, osiz);
bsiz += osiz;
buf[bsiz] = '\0';
*compressed_size = bsiz;
deflateEnd (&zs);
return buf;
}
char *
tracker_uncompress (const char *ptr, int size, int *uncompressed_size)
{
z_stream zs;
char *buf, *swap;
unsigned char obuf[ZLIBBUFSIZ];
int rv, asiz, bsiz, osiz;
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
if (inflateInit2 (&zs, 15) != Z_OK) return NULL;
asiz = size * 2 + 16;
if (asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
if (!(buf = malloc (asiz))) {
inflateEnd (&zs);
return NULL;
}
bsiz = 0;
zs.next_in = (unsigned char *)ptr;
zs.avail_in = size;
zs.next_out = obuf;
zs.avail_out = ZLIBBUFSIZ;
while ( (rv = inflate (&zs, Z_NO_FLUSH)) == Z_OK) {
osiz = ZLIBBUFSIZ - zs.avail_out;
if (bsiz + osiz >= asiz) {
asiz = asiz * 2 + osiz;
if (!(swap = realloc (buf, asiz))) {
free (buf);
inflateEnd (&zs);
return NULL;
}
buf = swap;
}
memcpy (buf + bsiz, obuf, osiz);
bsiz += osiz;
zs.next_out = obuf;
zs.avail_out = ZLIBBUFSIZ;
}
if (rv != Z_STREAM_END) {
free (buf);
inflateEnd (&zs);
return NULL;
}
osiz = ZLIBBUFSIZ - zs.avail_out;
if (bsiz + osiz >= asiz) {
asiz = asiz * 2 + osiz;
if (!(swap = realloc (buf, asiz))) {
free (buf);
inflateEnd (&zs);
return NULL;
}
buf = swap;
}
memcpy (buf + bsiz, obuf, osiz);
bsiz += osiz;
buf[bsiz] = '\0';
*uncompressed_size = bsiz;
inflateEnd (&zs);
return buf;
}
static inline gboolean
is_match (const char *a, const char *b)
{
int len = strlen (b);
char *str1 = g_utf8_casefold (a, len);
char *str2 = g_utf8_casefold (b, len);
char *normal1 = g_utf8_normalize (str1, -1, G_NORMALIZE_NFC);
char *normal2 = g_utf8_normalize (str2, -1, G_NORMALIZE_NFC);
gboolean result = (strcmp (normal1, normal2) == 0);
g_free (str1);
g_free (str2);
g_free (normal1);
g_free (normal2);
return result;
}
static const gchar *
pointer_from_offset_skipping_decomp (const gchar *str, gint offset)
{
gchar *casefold, *normal;
const gchar *p, *q;
g_return_val_if_fail (str != NULL, NULL);
p = str;
while (offset > 0)
{
q = g_utf8_next_char (p);
casefold = g_utf8_casefold (p, q - p);
normal = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFC);
offset -= g_utf8_strlen (normal, -1);
g_free (casefold);
g_free (normal);
p = q;
}
return p;
}
static const char *
g_utf8_strcasestr_array (const gchar *haystack, gchar **needles)
{
gsize needle_len;
gsize haystack_len;
const char *ret = NULL, *needle;
char **array;
char *p;
char *casefold;
char *caseless_haystack;
int i;
g_return_val_if_fail (haystack != NULL, NULL);
casefold = g_utf8_casefold (haystack, -1);
caseless_haystack = g_utf8_normalize (casefold, -1, G_NORMALIZE_NFC);
g_free (casefold);
if (!caseless_haystack) {
return NULL;
}
haystack_len = g_utf8_strlen (caseless_haystack, -1);
for (array=needles; *array; array++)
{
needle = *array;
needle_len = g_utf8_strlen (needle, -1);
if (needle_len == 0) {
continue;
}
if (haystack_len < needle_len) {
continue;
}
p = (gchar *) caseless_haystack;
needle_len = strlen (needle);
i = 0;
while (*p) {
if ((strncmp (p, needle, needle_len) == 0)) {
ret = pointer_from_offset_skipping_decomp (haystack, i);
goto finally_1;
}
p = g_utf8_next_char (p);
i++;
}
}
finally_1:
g_free (caseless_haystack);
return ret;
}
const char *
substring_utf8 (const char *a, const char *b)
{
const char *ptr, *found_ptr;
gunichar c, lower, upper;
int len;
gboolean got_match = FALSE;
len = strlen (b);
c = g_utf8_get_char (b);
lower = g_unichar_tolower (c);
upper = g_unichar_toupper (c);
ptr = a;
found_ptr = a;
/* check lowercase first */
while (found_ptr) {
found_ptr = g_utf8_strchr (ptr, -1, lower);
if (found_ptr) {
ptr = g_utf8_find_next_char (found_ptr, NULL);
if (is_match (found_ptr, b)) {
got_match = TRUE;
break;
}
} else {
break;
}
}
if (!got_match) {
ptr = a;
found_ptr = a;
while (found_ptr) {
found_ptr = g_utf8_strchr (ptr, -1, upper);
if (found_ptr) {
ptr = g_utf8_find_next_char (found_ptr, NULL);
if (is_match (found_ptr, b)) {
break;
}
} else {
}
}
}
return found_ptr;
}
static int
get_word_break (const char *a)
{
char **words = g_strsplit_set (a, "\t\n\v\f\r !\"#$%&'()*/<=>?[\\]^`{|}~+,.:;@\"[]" , -1);
if (!words) return 0;
int ret = strlen (words[0]);
g_strfreev (words);
return ret;
}
static gboolean
is_word_break (const char a)
{
const char *breaks = "\t\n\v\f\r !\"#$%&'()*/<=>?[\\]^`{|}~+,.:;@\"[]";
int i;
for (i = 0; breaks[i]; i++) {
if (a == breaks[i]) {
return TRUE;
}
}
return FALSE;
}
static char *
highlight_terms (const char *str, char **terms)
{
const char *ptr;
char *txt;
GString *st;
int term_length;
if (!str || !terms) {
return NULL;
}
char **array;
txt = g_strdup (str);
for (array = terms; *array; array++) {
char **single_term;
single_term = g_new( char *, 2);
single_term[0] = g_strdup (*array);
single_term[1] = NULL;
st = g_string_new ("");
const char *ptxt = txt;
while ((ptr = g_utf8_strcasestr_array (ptxt, single_term))) {
char *pre_snip, *term;
pre_snip = g_strndup (ptxt, (ptr - ptxt));
term_length = get_word_break (ptr);
term = g_strndup (ptr, term_length);
ptxt = ptr + term_length;
g_string_append_printf (st, "%s<b>%s</b>", pre_snip, term);
g_free (pre_snip);
g_free (term);
}
if (ptxt) {
g_string_append (st, ptxt);
}
g_strfreev (single_term);
g_free (txt);
txt = g_string_free (st, FALSE);
}
return txt;
}
char *
tracker_get_snippet (const char *txt, char **terms, int length)
{
const char *ptr = NULL, *end_ptr, *tmp;
int i, txt_len;
if (!txt || !terms) {
return NULL;
}
txt_len = strlen (txt);
ptr = g_utf8_strcasestr_array (txt, terms);
if (ptr) {
tmp = ptr;
i = 0;
/* get snippet before the matching term */
while ((ptr = g_utf8_prev_char (ptr)) && (ptr >= txt) && (i < length)) {
if (*ptr == '\n') {
break;
}
i++;
}
/* try to start beginning of snippet on a word break */
if ((*ptr != '\n') && (ptr > txt)) {
i=0;
while (!is_word_break (*ptr) && (i<(length/2))) {
ptr = g_utf8_next_char (ptr);
i++;
}
}
ptr = g_utf8_next_char (ptr);
if (!ptr || ptr < txt) {
return NULL;
}
end_ptr = tmp;
i = 0;
/* get snippet after match */
while ((end_ptr = g_utf8_next_char (end_ptr)) && (end_ptr <= txt_len + txt) && (i < length)) {
i++;
if (*end_ptr == '\n') {
break;
}
}
while (end_ptr > txt_len + txt) {
end_ptr = g_utf8_prev_char (end_ptr);
}
/* try to end snippet on a word break */
if ((*end_ptr != '\n') && (end_ptr < txt_len + txt)) {
i=0;
while (!is_word_break (*end_ptr) && (i<(length/2))) {
end_ptr = g_utf8_prev_char (end_ptr);
i++;
}
}
if (!end_ptr || !ptr) {
return NULL;
}
char *snip, *esc_snip, *highlight_snip;
snip = g_strndup (ptr, end_ptr - ptr);
i = strlen (snip);
esc_snip = g_markup_escape_text (snip, i);
g_free (snip);
highlight_snip = highlight_terms (esc_snip, terms);
g_free (esc_snip);
return highlight_snip;
}
ptr = txt;
i = 0;
while ((ptr = g_utf8_next_char (ptr)) && (ptr <= txt_len + txt) && (i < length)) {
i++;
if (*ptr == '\n') {
break;
}
}
if (ptr > txt_len + txt) {
ptr = g_utf8_prev_char (ptr);
}
if (ptr) {
char *snippet = g_strndup (txt, ptr - txt);
char *esc_snippet = g_markup_escape_text (snippet, ptr - txt);
char *highlight_snip = highlight_terms (esc_snippet, terms);
g_free (snippet);
g_free (esc_snippet);
return highlight_snip;
} else {
return NULL;
}
}
static inline void
output_log (const char *message)
{
FILE *fd;
time_t now;
char buffer1[64], buffer2[20];
char *output;
struct tm *loctime;
GTimeVal start;
if (message) {
g_print ("%s\n", message);
}
/* ensure file logging is thread safe */
g_mutex_lock (tracker->log_access_mutex);
fd = g_fopen (tracker->log_file, "a");
if (!fd) {
g_mutex_unlock (tracker->log_access_mutex);
g_warning ("could not open %s", tracker->log_file);
return;
}
g_get_current_time (&start);
now = time ((time_t *) NULL);
loctime = localtime (&now);
strftime (buffer1, 64, "%d %b %Y, %H:%M:%S:", loctime);
g_sprintf (buffer2, "%03ld", start.tv_usec / 1000);
output = g_strconcat (buffer1, buffer2, " - ", message, NULL);
g_fprintf (fd, "%s\n", output);
g_free (output);
fclose (fd);
g_mutex_unlock (tracker->log_access_mutex);
}
void
tracker_error (const char *message, ...)
{
va_list args;
char *msg;
va_start (args, message);
msg = g_strdup_vprintf (message, args);
va_end (args);
output_log (msg);
g_free (msg);
if (tracker->fatal_errors) {
g_assert (FALSE);
}
}
void
tracker_log (const char *message, ...)
{
va_list args;
char *msg;
if (tracker->verbosity < 1) {
return;
}
va_start (args, message);
msg = g_strdup_vprintf (message, args);
va_end (args);
output_log (msg);
g_free (msg);
}
void
tracker_info (const char *message, ...)
{
va_list args;
char *msg;
if (tracker->verbosity < 2) {
return;
}
va_start (args, message);
msg = g_strdup_vprintf (message, args);
va_end (args);
output_log (msg);
g_free (msg);
}
void
tracker_debug (const char *message, ...)
{
va_list args;
char *msg;
if (tracker->verbosity < 3) {
return;
}
va_start (args, message);
msg = g_strdup_vprintf (message, args);
va_end (args);
output_log (msg);
g_free (msg);
}
char *
tracker_string_replace (const char *haystack, char *needle, char *replacement)
{
GString *str;
int pos, needle_len;
g_return_val_if_fail (haystack && needle, NULL);
needle_len = strlen (needle);
str = g_string_new ("");
for (pos = 0; haystack[pos]; pos++)
{
if (strncmp (&haystack[pos], needle, needle_len) == 0)
{
if (replacement) {
str = g_string_append (str, replacement);
}
pos += needle_len - 1;
} else {
str = g_string_append_c (str, haystack[pos]);
}
}
return g_string_free (str, FALSE);
}
gboolean
tracker_using_battery (void)
{
if (!tracker->battery_state_file) {
return FALSE;
}
char *txt;
gboolean using_battery;
if (!g_file_get_contents (tracker->battery_state_file, &txt, NULL, NULL)) {
return FALSE;
} else {
using_battery = (strstr (txt, "on-line") == NULL);
}
g_free (txt);
return using_battery;
}
void
tracker_add_metadata_to_table (GHashTable *meta_table, const char *key, const char *value)
{
GSList *list;
list = g_hash_table_lookup (meta_table, (char *) key);
list = g_slist_prepend (list, (char *) value);
g_hash_table_steal (meta_table, key);
g_hash_table_insert (meta_table, (char *) key, list);
}
void
tracker_free_metadata_field (FieldData *field_data)
{
g_return_if_fail (field_data);
if (field_data->alias) {
g_free (field_data->alias);
}
if (field_data->where_field) {
g_free (field_data->where_field);
}
if (field_data->field_name) {
g_free (field_data->field_name);
}
if (field_data->select_field) {
g_free (field_data->select_field);
}
if (field_data->table_name) {
g_free (field_data->table_name);
}
if (field_data->id_field) {
g_free (field_data->id_field);
}
g_free (field_data);
}
gboolean
tracker_unlink (const char *uri)
{
char *locale_uri = g_filename_from_utf8 (uri, -1, NULL, NULL, NULL);
if (!g_file_test (locale_uri, G_FILE_TEST_EXISTS)) {
g_free (locale_uri);
return FALSE;
}
g_unlink (locale_uri);
g_free (locale_uri);
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1