src/cmd/fsexam/src/fsexam-dryrun.c
author yz157939@agc105
Fri, 25 Apr 2008 17:13:57 +0800
changeset 149 0014c9b031e9
parent 147 8c4ef02c14b8
permissions -rw-r--r--
add fsexam 0.8.1

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <glib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include <glib/gi18n.h>
#include <gtk/gtk.h>

#include "fsexam-debug.h"
#include "fsexam-header.h"

void        cb_text_buffer_changed (GtkTextBuffer *buffer, gpointer data);

static void fsexam_dryrun_base_init (gpointer g_class);

static void dryrun_file_instance_init (GTypeInstance *instance, 
        gpointer g_class);
static void dryrun_file_interface_init (gpointer g_iface, 
        gpointer iface_data);
static void dryrun_file_class_init (GObjectClass *klass);

static void dryrun_buffer_instance_init (GTypeInstance *instance,
        gpointer g_class);
static void dryrun_buffer_interface_init (gpointer g_iface,
        gpointer iface_data);
static void dryrun_buffer_class_init (GObjectClass *klass);

static gboolean fsexam_dryrun_write_path (FsexamDryrun *, const gchar *);

static gboolean fsexam_dryrun_write_candidate (FsexamDryrun *info,
        const gchar *encoding_name,
        const gchar *sample);

/* FsexamDryrun Interface Implementation */
static void
fsexam_dryrun_base_init (gpointer g_class)
{
    static gboolean initialized = FALSE;

    if (!initialized) {
        initialized = TRUE;
    }

    return;
}

GType
fsexam_dryrun_get_type (void)
{
    static GType type = 0;

    if (type == 0) {
        static const GTypeInfo info = {
            sizeof (FsexamDryrunInterface),
            fsexam_dryrun_base_init,    /* base_init */
            NULL,                       /* bsae_finalize */
            NULL,                       /* class_init */
            NULL,                       /* class_finalize */
            NULL,                       /* class_data */
            0,                          /* sizeof (Instance) */
            0,                          /* n_preallocs */
            NULL,                       /* instance_init */
        };

        type = g_type_register_static (G_TYPE_INTERFACE, 
                "FsexamDryrun", 
                &info, 
                0);
    }

    return type;
}

gboolean
fsexam_dryrun_write (FsexamDryrun *self, const gchar *string)
{
    return FSEXAM_DRYRUN_GET_INTERFACE (self)->write (self, string);
}

gboolean
fsexam_dryrun_read (FsexamDryrun *self, gchar *buf, guint size)
{
    return FSEXAM_DRYRUN_GET_INTERFACE (self)->read (self, buf, size);
}

/* --- Dryrun implementation on file --- */
static void
dryrun_file_finalize (GObject *object)
{
    FsexamDryrunFile *dryrun_file = FSEXAM_DRYRUN_FILE (object);

    if (dryrun_file && dryrun_file->fp) {
        fclose (dryrun_file->fp);
        dryrun_file->fp = NULL;
    }

    return;
}

GType
fsexam_dryrun_file_get_type (void)
{
    static GType type = 0;

    if (type == 0) {
        static const GTypeInfo info = {
            sizeof (FsexamDryrunFileClass),
            NULL,   /* base_init */
            NULL,   /* base_finalize */
            (GClassInitFunc) dryrun_file_class_init, 
            NULL,   /* class_finalize */
            NULL,   /* class_data */
            sizeof (FsexamDryrunFile),
            0,      /* n_preallocs */
            (GInstanceInitFunc) dryrun_file_instance_init, 
        };

        static const GInterfaceInfo dryrun_info = {
            (GInterfaceInitFunc) dryrun_file_interface_init,/* interface_init */
            NULL,   /* interface_finalize */
            NULL,   /* interface_data */
        };

        type = g_type_register_static (G_TYPE_OBJECT, "FsexamDryrunFile", &info, 0);
        g_type_add_interface_static (type, FSEXAM_TYPE_DRYRUN, &dryrun_info);
    }

    return type;
}

/* write callback implementation of DryrunFile */
static gboolean 
dryrun_file_write (FsexamDryrunFile *self, const gchar *string)
{
    gboolean ret = TRUE;

    if (fputs (string, self->fp) < 0)
        ret = FALSE;

    return ret;
}

static gboolean
dryrun_file_read (FsexamDryrunFile *self, gchar *buf, guint size)
{
    gboolean ret = TRUE;

    if (fgets (buf, size, self->fp) == NULL)
        ret = FALSE;

    return ret;
}

static void
dryrun_file_interface_init (gpointer g_iface, gpointer iface_data)
{
    FsexamDryrunInterface *iface = (FsexamDryrunInterface *)g_iface;

    iface->write = (gboolean (*) (FsexamDryrun *, const gchar *))dryrun_file_write;
    iface->read = (gboolean (*) (FsexamDryrun *, gchar *, guint)) dryrun_file_read;

    return;
}

static void
dryrun_file_instance_init (GTypeInstance *instance, gpointer g_class)
{
    FsexamDryrunFile *self = FSEXAM_DRYRUN_FILE (instance);

    self->fp = NULL;

    return;
}

static void
dryrun_file_class_init (GObjectClass *klass)
{
    klass->finalize = dryrun_file_finalize;

    return;
}

FsexamDryrun *
fsexam_dryrun_file_new (const gchar *filename, gboolean readonly)
{
    FsexamDryrunFile *dryrun = NULL;

    if (filename == NULL)
        return NULL;

    dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_FILE, NULL);
    dryrun->fp = fopen (filename, readonly ? "r" : "w");

    if (dryrun->fp == NULL) {
        g_object_unref (dryrun);
        return NULL;
    }

    return FSEXAM_DRYRUN (dryrun);
}

/* --- Widget implementation for dryrun --- */
GType
fsexam_dryrun_buffer_get_type (void)
{
    static GType type = 0;

    if (type == 0) {
        static const GTypeInfo info = {
            sizeof (FsexamDryrunBufferClass),
            NULL,   /* base_init */
            NULL,   /* base_finalize */
            (GClassInitFunc) dryrun_buffer_class_init, 
            NULL,   /* class_finalize */
            NULL,   /* class_data */
            sizeof (FsexamDryrunBuffer),
            0,      /* n_preallocs */
            (GInstanceInitFunc) dryrun_buffer_instance_init, 
        };

        static const GInterfaceInfo dryrun_info = {
            (GInterfaceInitFunc) dryrun_buffer_interface_init,
            NULL,   /* interface_finalize */
            NULL,   /* interface_data */
        };

        type = g_type_register_static (G_TYPE_OBJECT, "FsexamDryrunBuffer", &info, 0);
        g_type_add_interface_static (type, FSEXAM_TYPE_DRYRUN, &dryrun_info);
    }

    return type;
}

/*
 * Create mark and connect signal handler for GtkTextBuffer 
 */
static void
setup_text_buffer (GtkTextBuffer *buffer)
{
    GtkTextIter iter;

    if (buffer == NULL)
        return;

    gtk_text_buffer_get_end_iter (buffer, &iter);
    gtk_text_buffer_create_mark (buffer, "end_mark", &iter, FALSE);

    g_signal_connect (G_OBJECT (buffer), "changed", 
                      G_CALLBACK (cb_text_buffer_changed), NULL);

    return;
}

static gboolean 
dryrun_buffer_write (FsexamDryrunBuffer *self, const gchar *string)
{
    GtkTextIter     iter;

    if (self->buffer == NULL)
        return FALSE;

    gtk_text_buffer_get_end_iter (self->buffer, &iter);
    gtk_text_buffer_insert (self->buffer, &iter, string, -1);

    return TRUE;
}

/*
 * read one line from text buffer and increase line number
 */
static gboolean
dryrun_buffer_read (FsexamDryrunBuffer *self, gchar *buf, guint size)
{
    gboolean    ret = TRUE;
    guint       line_count;
    GtkTextIter start, end;
    gchar       *text = NULL;

    line_count = (guint) gtk_text_buffer_get_line_count (self->buffer);
    line_count --;  /* The last line is empty line */

    if (self->current_line >= line_count) /* EOF */
        return FALSE;

    gtk_text_buffer_get_iter_at_line (self->buffer, 
            &start, 
            self->current_line);
    gtk_text_buffer_get_iter_at_line (self->buffer, 
            &end, 
            self->current_line + 1);

    text = gtk_text_buffer_get_text (self->buffer, &start, &end, FALSE);

    strlcpy (buf, text, size);

    g_free (text);
    self->current_line++;

    return ret;
}

static void
dryrun_buffer_interface_init (gpointer g_iface, gpointer iface_data)
{
    FsexamDryrunInterface *iface = (FsexamDryrunInterface *)g_iface;

    iface->write = (gboolean (*) (FsexamDryrun *, const gchar *))dryrun_buffer_write;
    iface->read = (gboolean (*) (FsexamDryrun *, gchar *, guint)) dryrun_buffer_read;

    return;
}

static void
dryrun_buffer_instance_init (GTypeInstance *instance, gpointer g_class)
{
    FsexamDryrunBuffer *self = FSEXAM_DRYRUN_BUFFER (instance);

    self->buffer = NULL;
    self->current_line = 0;

    return;
}

static void 
dryrun_buffer_finalize (GObject *object)
{
    return;
}

static void
dryrun_buffer_class_init (GObjectClass *klass)
{
    klass->finalize = dryrun_buffer_finalize; 

    return;
}

FsexamDryrun *
fsexam_dryrun_buffer_new (void)
{
    FsexamDryrunBuffer *dryrun = NULL;

    dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_BUFFER, NULL);

    return FSEXAM_DRYRUN (dryrun);
}

FsexamDryrun *
fsexam_dryrun_buffer_new_with_buffer (GtkTextBuffer *buffer)
{
    FsexamDryrunBuffer *dryrun = NULL;

    dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_BUFFER, NULL);

    if (buffer != NULL) {
        dryrun->buffer = buffer;
        setup_text_buffer (buffer);
    }

    return FSEXAM_DRYRUN (dryrun);
}

void
fsexam_dryrun_buffer_clear_buffer (FsexamDryrunBuffer *buffer)
{
    GtkTextIter start, end;

    gtk_text_buffer_get_start_iter (buffer->buffer, &start);
    gtk_text_buffer_get_end_iter (buffer->buffer, &end);
    gtk_text_buffer_delete (buffer->buffer, &start, &end);

    buffer->current_line = 0;

    return;
}

void 
fsexam_dryrun_buffer_set_buffer (FsexamDryrunBuffer *buffer, 
        GtkTextBuffer *buf)
{
    if (buffer == NULL)
        return;

    buffer->buffer = buf;
    setup_text_buffer (buffer->buffer);
    
    return;
}

void 
fsexam_dryrun_buffer_set_current_line (FsexamDryrunBuffer *buffer, guint line_no)
{
    if (buffer == NULL)
        return;

    buffer->current_line = line_no;

    return;
}

/*
 * Write the full file path into dryrun file
 */
static gboolean 
fsexam_dryrun_write_path (FsexamDryrun *info, const gchar *path)
{
    if ((NULL == path) || (NULL == info))
        return FALSE;

    if (g_utf8_validate (path, -1, NULL)){
        if (! fsexam_dryrun_write (info, path)){
            fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
            goto fail;
        }
    }else{
        gchar *uri = NULL;
        uri = g_filename_to_uri (path, NULL, NULL);
        if (NULL == uri){
            fsexam_errno = ERR_CANNOT_CONVERT_TO_URI;
            goto fail;
        }
        if (! fsexam_dryrun_write (info, uri) < 0){
            fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
            g_free (uri);
            goto fail;
        }
        g_free (uri);
    }

    fsexam_dryrun_write (info, "\n");
    
    return TRUE;

fail:
    return FALSE;
}

static gboolean
fsexam_dryrun_write_candidate (FsexamDryrun *info, 
                               const gchar *encoding_name, 
                               const gchar *sample)
{
    if ((NULL == info) || (NULL == sample) || (NULL == encoding_name))
        return FALSE;

    if (! fsexam_dryrun_write (info, "\t")) {
        fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
        return FALSE;
    }

    fsexam_dryrun_write (info, encoding_name);
    fsexam_dryrun_write (info, "\t");
    fsexam_dryrun_write (info, sample);
    fsexam_dryrun_write (info, "\n");

    return TRUE;
}

gboolean
fsexam_dryrun_write_msg (FsexamDryrun *info, const gchar *msg)
{
    if ((NULL == info) || (NULL == msg))
        return FALSE;

    if (! fsexam_dryrun_write (info, "\t")) {
        fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
        return FALSE;
    }

    fsexam_dryrun_write (info, msg);
    fsexam_dryrun_write (info, "\n");

    return TRUE;
}

gboolean 
fsexam_dryrun_write_convtype (FsexamDryrun *info, ConvType type)
{
    g_return_val_if_fail ((info != NULL), FALSE);

    if (type == ConvName)
        fsexam_dryrun_write (info, "Name conversion\n");
    else
        fsexam_dryrun_write (info, "Content conversion\n");

    return TRUE;
}

/*
 * Get the conversion type, and set fsexam_errno if invalid
 */
gboolean
fsexam_dryrun_get_convtype (FsexamDryrun *info, ConvType *type)
{
    const gint TYPE_LENGTH = 80;
    gchar convtype[TYPE_LENGTH];
    gchar *strip = NULL;

    if (! fsexam_dryrun_read (info, convtype, sizeof (convtype))){
        fsexam_errno = ERR_DRYRUN_FILE_INVALID;
        return FALSE;
    }

    strip = g_strstrip (convtype);

    if (strcmp (strip, "Name conversion") == 0)
        *type = ConvName;
    else if (strcmp (strip, "Content conversion") == 0)
        *type = ConvContent;
    else
        fsexam_errno = ERR_DRYRUN_FILE_INVALID;

    return TRUE;
}

/* 
 *  Analyze dryrun result file, add Dryrun_item which represent
 *  user selected encoding into GSList result.
 */
gboolean
fsexam_dryrun_process (FsexamDryrun *info, GSList **result)
{
    g_return_val_if_fail (info != NULL, FALSE);

    gchar    linebuf [PATH_MAX + 20];
    gchar    *path = NULL, *encoding = NULL, *sample = NULL, *ptr = NULL;
    GSList   *slist = NULL;
    gboolean metpath = FALSE, metcode = FALSE, tab = FALSE;
    gboolean ret = FALSE;

    while (fsexam_dryrun_read (info, linebuf, sizeof (linebuf))) {
        if (linebuf[strlen (linebuf) - 1] == '\n')
            linebuf[strlen (linebuf) - 1] = '\0';

        /* remove leading and trailing white space */
        if ((ptr = str_compress (linebuf, &tab)) == NULL) {
            continue;                       /* Empty line */
        }

        /* 
         * Some files may not need convert at all, or have no 
         * proper encoding. bypass it and continue.
         */
        if ((strcmp (ptr, FSEXAM_DRYRUN_NO_NEED_CONVERT) == 0)
                || (strcmp (ptr, FSEXAM_DRYRUN_NO_PROPER_ENCODING) == 0)) {
            if (!metpath) {
                fsexam_errno = ERR_DRYRUN_FILE_INVALID;
                goto done;
            }

            g_free (path);
            path = NULL;
            metpath = FALSE;
            continue;
        }
        
        if (tab) {
            Dryrun_item *item = NULL;

            if (!metpath) {
                if (metcode) {      /* metpath will be false after first candidate */
                    continue;       /* ignore non-first encoding candidate */
                }else{
                    fsexam_errno = ERR_DRYRUN_FILE_INVALID;
                    goto done;
                }
            }

            metcode = TRUE;

            str_split (ptr, &encoding, &sample);
            if ((encoding2id (encoding) == -1) || (sample == NULL)) {
                fsexam_errno = ERR_DRYRUN_FILE_INVALID;
                goto done;
            }

            /*
             * Found one valid path/encoding pair.
             */
            if ((item = g_new0 (Dryrun_item, 1)) == NULL) {
                fsexam_errno = ERR_NO_MEMORY;
                goto done;
            }

            item->path = path;
            item->encoding = encoding;

            slist = g_slist_prepend (slist, item);

            g_free (sample);
            path = NULL;
            encoding = NULL;
            sample = NULL;

            metpath = FALSE;
        }else{
            if (metpath) {
                fsexam_errno = ERR_DRYRUN_FILE_INVALID;
                goto done;
            }

            if ('/' == *ptr) {
                path = g_strdup (ptr);
            }else{
                path = g_filename_from_uri (ptr, NULL, NULL);
            }

            if (NULL == path) {
                fsexam_errno = ERR_DRYRUN_FILE_INVALID;
                goto done;
            }

            metpath = TRUE;
            metcode = FALSE;
        }
    }

    if (NULL == slist)
        fsexam_errno = ERR_DRYRUN_FILE_INVALID;
    else
        slist = g_slist_reverse (slist);

    *result = slist;        /* caller must pass in this param */
    ret = TRUE;

done:
    g_free (path);
    g_free (encoding);
    g_free (sample);

    return ret;
}

void
fsexam_dryrun_item_slist_free (GSList *slist)
{
    if (NULL == slist)
        return;

    while (slist != NULL) {
        Dryrun_item *item = slist->data;

        g_free (item->path);
        g_free (item->encoding);
        g_free (item);

        slist = slist->next;
    }

    g_slist_free (slist);

    return;
}

/* 
 * Write one dryrun item into file
 */
gboolean
fsexam_dryrun_puts (FsexamDryrun *info, 
                    const gchar *fullpath, 
                    Score score,            /* total score */
                    GList *encoding_list, 
                    ConvType convtype)
{
    gboolean    ret = TRUE;

    if ((NULL == info) || (NULL == fullpath) || (NULL == encoding_list))
        return FALSE;

    if (! fsexam_dryrun_write_path (info, fullpath)) {
        return FALSE;
    }

    if (score == FAIL){
        if (! fsexam_dryrun_write_msg (info, 
                                      _(FSEXAM_DRYRUN_NO_PROPER_ENCODING))){
            fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
            ret = FALSE;
        }
    }else if (score == ORIGINAL){
        if (! fsexam_dryrun_write_msg (info, 
                                       _(FSEXAM_DRYRUN_NO_NEED_CONVERT))) {
            fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
            ret = FALSE;
        }
    }else{
        while (encoding_list) {
            Encoding  *encoding = NULL;
            gchar     *sample = NULL;
            
            encoding = (Encoding *)encoding_list->data; 
            encoding_list = g_list_next (encoding_list);

            if ((encoding->score == FAIL) || (encoding->score == ORIGINAL)){
                continue;
            }

            if (convtype == ConvName) {
                sample = encoding->u.converted_text;
            }else{
                sample = encoding->u.contents;
            }

            if (! fsexam_dryrun_write_candidate (info, 
                                    id2encoding (encoding->encodingID),
                                    sample)) {
                fsexam_errno = ERR_CANNOT_WRITE_DRYRUN;
                ret = FALSE;
            }
/*      BIG bug: double free
            if (convtype != ConvName)
                g_free (sample);
*/
        }
    }

    return ret;
}