Home | History | Annotate | Download | only in src
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 #ifdef HAVE_CONFIG_H
     27 #include <config.h>
     28 #endif
     29 
     30 #include <locale.h>
     31 #include <strings.h>
     32 #include <string.h>
     33 #include <unistd.h>
     34 #include <libgen.h>
     35 #include <stdlib.h>
     36 #include <sys/types.h>
     37 #include <dirent.h>
     38 
     39 #include <glib.h>
     40 #include <glib/gi18n.h>
     41 
     42 #include "fsexam-header.h"
     43 #include "fsexam-specialfile.h"
     44 
     45 /* -------------  Compress file type implementation ------------ */
     46 typedef enum {
     47     COMP_TARGZ = 0,
     48     COMP_TARBZ2,
     49     COMP_TAR,
     50     COMP_ZIP,
     51     COMP_Z,
     52 }Compress_Type;
     53 
     54 typedef struct {
     55     gchar *result_path;      /* used to generate hist path */
     56     gchar *search_path;      /* used to generate hist search path */
     57     gchar *log_path;         /* used to generate log_path */
     58     gint  temp_dir_len;
     59 }Compress_info;
     60 
     61 static gchar    *iso8859_locale = NULL;
     62 const gchar *compress_pair[][3] = {
     63     /* Type         UNCOMPRESS                 COMPRESS */
     64     {".tar.gz",  "LC_ALL=%s gzcat %s | tar -xf - 2>/dev/null",    "LC_ALL=%s tar -cf - . | gzip > ../%s"},
     65     {".tar.bz2", "LC_ALL=%s bzcat %s | tar -xf - 2>/dev/null",    "LC_ALL=%s tar -cf - . | bzip2 -z > ../%s"},
     66     {".tar",     "LC_ALL=%s tar -xf %s 2>/dev/null",              "LC_ALL=%s tar -cf - . > ../%s"},
     67     {".zip",     "unzip -q %s 2>/dev/null",             "zip -qr %s .; mv %s .."},
     68     {".tar.Z",   "uncompress -c %s | tar -xf - 2>/dev/null", "tar -cf - . | compress -fc > ../%s"},
     69 };
     70 
     71 #define TARCMD  "/usr/sfw/bin/gtar"
     72 
     73 static gboolean fsexam_special_is_compress (const gchar *, Compress_Type *);
     74 static gboolean compress_common_convert (const gchar *,
     75         Compress_info *,
     76         FSEXAM_setting *,
     77         gboolean restore,
     78         gboolean nameconvert);
     79 static gboolean compress_convert_content (const gchar *,
     80         Compress_info *,
     81         FSEXAM_setting *,
     82         gboolean restore);
     83 
     84 static Compress_info *
     85 compress_info_new (const gchar *result_path,
     86         const gchar *search_path,
     87         const gchar *log_path)
     88 {
     89     Compress_info *info = NULL;
     90 
     91     if (NULL == result_path)
     92         return NULL;
     93 
     94     info = g_new0 (Compress_info, 1);
     95     info->result_path = g_strdup (result_path);
     96     info->search_path = g_strdup (search_path ? search_path : result_path);
     97     info->log_path = g_strdup (log_path ? log_path : result_path);
     98 
     99     return info;
    100 }
    101 
    102 static void
    103 compress_info_free (Compress_info *info)
    104 {
    105     if (info == NULL) {
    106         return;
    107     }
    108 
    109     g_free (info->result_path);
    110     g_free (info->search_path);
    111     g_free (info->log_path);
    112     g_free (info);
    113 
    114     return;
    115 }
    116 
    117 /*
    118  *  ensure $PWD is the directory of temparay directory
    119  */
    120 static gchar *
    121 compose_compress_cmd (Compress_Type type, const gchar *bname)
    122 {
    123     if (type == COMP_ZIP) {
    124         return g_strdup_printf (compress_pair[type][2], bname, bname);
    125     }
    126 
    127     if (type <= COMP_TAR) {
    128         return g_strdup_printf (compress_pair[type][2], iso8859_locale, bname);
    129     }
    130 
    131     return g_strdup_printf (compress_pair[type][2], bname);
    132 }
    133 
    134 /*
    135  * Generate history path or log path for compress file.
    136  *      Please note that we may nested to handling compress file. so
    137  *      orig_compress_file
    138  *      may be in the form "/tmp/test/test.tar[src/test.tar][cmd/data.tar]
    139  *                          -------------------------------  ------------
    140  *                          orig_compress_file               fullpath - temp..
    141  *
    142  *      fullpath: the full path of compress file in question such as /tmp/a.tar
    143  *
    144  * The sequence of converting compress filename and content:
    145  *      regurlar: first file name , then file content. otherwise the log/hist
    146  *                info is not consistent with disk data.
    147  *      resotre ; first filecontent, then file name itself, otherwise
    148  *                        can't search the history  correctly
    149  */
    150 static gchar *
    151 compose_path (const gchar *fullpath,
    152         Compress_info *info,
    153         gboolean forhist,
    154         gboolean restore)
    155 {
    156     const gchar *relative_path;
    157     gchar       *result = NULL;
    158 #define URI_HEAD_LEN    7
    159 
    160     if ((NULL == fullpath) || (NULL == info))
    161         return NULL;
    162 
    163     relative_path = fullpath + info->temp_dir_len + 1;
    164 
    165     if (forhist) {
    166         /*
    167          *  Convert hist_path to uri, remove 'file://' then concat with
    168          *  relative_path
    169          */
    170         gchar *uri;
    171 
    172         uri = g_filename_to_uri (
    173                 restore ? info->search_path : info->result_path,
    174                 NULL,
    175                 NULL);
    176         result = g_strdup_printf ("%s\t%s",
    177                 uri + URI_HEAD_LEN,
    178                 relative_path);
    179 
    180         g_free (uri);
    181     }else {
    182         gchar *esc_path = NULL;
    183 
    184         if (! g_utf8_validate (relative_path, -1, NULL)) {
    185             esc_path = g_strescape (relative_path, NULL);
    186         }
    187 
    188         if (g_utf8_validate (info->log_path, -1, NULL)) {
    189             result = g_strdup_printf ("%s [%s]",
    190                     info->log_path,
    191                     esc_path ? esc_path : relative_path);
    192         }else{
    193             gchar *uri = g_filename_to_uri (info->log_path, NULL, NULL);
    194             result = g_strdup_printf ("%s [%s]",
    195                     uri,
    196                     esc_path ? esc_path : relative_path);
    197             g_free (uri);
    198         }
    199     }
    200 
    201     return result;
    202 }
    203 
    204 /*
    205  *  caller pass in orig_compress_file;
    206  */
    207 static gboolean
    208 compress_write_to_disk (FSEXAM_setting *setting,
    209             const gchar *path,      /* the path of current file */
    210             const gchar *origname,  /* old base name            */
    211             const gchar *utf8name,  /* new base name            */
    212             Compress_info *info,
    213             short from_encoding,    /* from encoding            */
    214             short to_encoding,      /* to encoding              */
    215             gchar **actualname)      /* return the actual used   */
    216 {
    217     static gchar    oldname[PATH_MAX];
    218     static gchar    newname[PATH_MAX];
    219     gchar           *retname = NULL;
    220     gchar           *fname = NULL;
    221     gchar           *log_path = NULL;
    222     gboolean        ret = FALSE;
    223 
    224     fsexam_errno = ERR_OK;
    225 
    226     /* construct full old name and full new name */
    227     g_snprintf (oldname, PATH_MAX - 1, "%s/%s", path, origname);
    228     g_snprintf (newname, PATH_MAX - 1, "%s/%s", path, utf8name);
    229 
    230     if (g_file_test (newname, G_FILE_TEST_EXISTS)) {
    231         fsexam_errno = ERR_NAME_EXIST;
    232         fname = find_non_exist_name (newname);
    233 
    234         if (fname == NULL) {
    235             fsexam_errno = ERR_CANNOT_RENAME;
    236         }else if (rename(oldname, fname) == 0){
    237             retname = g_strdup (basename (fname));
    238             ret = TRUE;
    239         }else{
    240             fsexam_errno = ERR_CANNOT_RENAME;
    241         }
    242     }else{
    243         if (rename(oldname, newname) == 0){
    244             retname = g_strdup (utf8name);
    245             ret = TRUE;
    246         }else{
    247             fsexam_errno = ERR_CANNOT_RENAME;
    248         }
    249     }
    250 
    251     /* history file; GUI vs. CLI */
    252     if ((ret)  && (! (setting->flags & FSEXAM_SETTING_FLAGS_UNDO))) {
    253         gboolean same_serial = TRUE;
    254         if (setting->flags & FSEXAM_SETTING_FLAGS_DIFF_SERIAL) {
    255             setting->flags &= ~FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
    256             same_serial = FALSE;
    257         }
    258 
    259         gchar *hist_path =  compose_path (newname, info, TRUE, FALSE);
    260 
    261         fsexam_history_put (setting->hist_info,
    262                             ConvNameSpecial,
    263                             hist_path,
    264                             from_encoding,
    265                             to_encoding,
    266                             same_serial);
    267 
    268         g_free (hist_path);
    269     }
    270 
    271     ret ? ++setting->succ_num : ++setting->fail_num;
    272 
    273     log_path = compose_path (ret ? newname : oldname, info, FALSE, FALSE);
    274 
    275     /* log file */
    276     if (fsexam_errno == ERR_OK) {
    277         gchar   *msg = g_strdup_printf ("%s -> %s",
    278                         id2encoding (from_encoding),
    279                         id2encoding (to_encoding));
    280         fsexam_log_puts (setting->log_info, log_path, msg);
    281         if (setting->display_msg) {
    282             setting->display_msg (log_path, msg);
    283         }
    284         g_free (msg);
    285     }else if (fsexam_errno == ERR_NAME_EXIST) {
    286         fsexam_log_puts (setting->log_info, log_path, NULL);
    287         if (setting->display_msg) {
    288             setting->display_msg (log_path, fsexam_error_get_msg ());
    289         }
    290     }else{
    291         fsexam_log_puts (setting->log_info, log_path, NULL);
    292         if (setting->display_msg) {
    293             setting->display_msg (log_path, fsexam_error_get_msg ());
    294         }
    295 
    296     }
    297 
    298     if (actualname != NULL)
    299         *actualname = retname;  //freed outside
    300     else
    301         g_free (retname);
    302 
    303     g_free (log_path);
    304     g_free (fname);
    305 
    306     return ret;
    307 }
    308 
    309 /*
    310  * Restore single file.
    311  *
    312  * Note that search path is in info directly, don't use compose any more.
    313  * This is diff with non-restore operation.
    314  */
    315 static gboolean
    316 compress_restore_single_filename (FSEXAM_setting *setting,
    317         const gchar *fullpath,
    318         const gchar *dname,
    319         const gchar *bname,
    320         Compress_info *info,
    321         gchar **newname)
    322 {
    323     gboolean ret = FALSE;
    324     Hist_item *item = NULL;
    325     gchar     *converted_text = NULL;
    326     gchar     *log_path;
    327 
    328     fsexam_errno = ERR_OK;
    329 
    330     item = fsexam_history_search (setting->hist_info, info->search_path, TRUE);
    331 
    332     if ((item == NULL) || (item->convtype != ConvNameSpecial)) {
    333         fsexam_errno = ERR_HIST_NO_ITEM;
    334         goto done;
    335     }
    336 
    337     converted_text = g_convert (bname,
    338             strlen (bname),
    339             id2encoding (item->from_encoding),
    340             id2encoding (item->to_encoding),
    341             NULL,
    342             NULL,
    343             NULL);
    344 
    345     if (converted_text == NULL) {
    346         fsexam_errno = ERR_CANNOT_CONVERT;
    347         goto done;
    348     }
    349 
    350     ret = compress_write_to_disk (setting,
    351             dname,
    352             bname,
    353             converted_text,
    354             info,
    355             item->to_encoding,
    356             item->from_encoding,
    357             newname);
    358 
    359     g_free (converted_text);
    360 
    361     return ret;
    362 
    363 done:
    364     log_path = compose_path (fullpath, info, FALSE, FALSE);
    365     fsexam_log_puts (setting->log_info, log_path, NULL);
    366     if (setting->display_msg)
    367         setting->display_msg (log_path, fsexam_error_get_msg());
    368 
    369     g_free (log_path);
    370 
    371 
    372     return ret;
    373 }
    374 
    375 /*
    376  *
    377  */
    378 static gboolean
    379 compress_convert_single_filename (FSEXAM_setting *setting,
    380         const gchar *fullpath,
    381         const gchar *dname, /* info->log path, info->dname */
    382         const gchar *bname,
    383         Compress_info *info,
    384         gchar **newname)
    385 {
    386     Score       score;
    387     Encoding    *encoding = NULL;
    388     gboolean    ret = FALSE;
    389 
    390     fsexam_errno = ERR_OK;  /* initialize fsexam_errno */
    391 
    392     if (setting->pref->auto_detect) { /* handle encoding auto detection */
    393         GList *detected_encoding = str_encoding_detect (bname,
    394                 DEFAULT_DETECTING_FLAG);
    395         setting->pref->encode_list = fsexam_encoding_add_auto (
    396                 setting->pref->encode_list,
    397                 detected_encoding);
    398         auto_encoding_free (detected_encoding);
    399     }
    400 
    401     score = fsexam_encoding_decode (setting->pref->encode_list,
    402                              ConvName,
    403                              (char *)bname,
    404                              strlen(bname),
    405                              setting->pref->force);
    406 
    407 
    408     if (setting->pref->dry_run){    /* dry run */
    409         //don't support dryrun
    410     } else {                    /* real convert */
    411         gint     index = 0;
    412         gchar    *actualname = NULL;
    413 
    414         if ((score == FAIL) || (score == ORIGINAL)){
    415             fsexam_errno = (score == FAIL) ? ERR_NO_PROPER_ENCODING
    416                                            : ERR_NAME_UTF8_ALREADY;
    417 
    418             goto done;
    419         }
    420 
    421         if (setting->gold_index != -1) {
    422             index = setting->gold_index;
    423 	    } else if (setting->pref->auto_conversion) {
    424             index = fsexam_encoding_get_first_index (
    425                     setting->pref->encode_list);
    426         } else {
    427             index = setting->get_index (setting->pref->encode_list, TRUE);
    428         }
    429 
    430         if (-1 == index) {
    431             goto cleanup;
    432         }
    433 
    434         encoding = (Encoding *)g_list_nth_data (setting->pref->encode_list,
    435                 index);
    436         if (NULL == encoding){
    437             fsexam_errno = ERR_ENCODING_INDEX_INVALID;
    438             goto done;
    439         }
    440 
    441         ret = compress_write_to_disk (setting,
    442                 dname,
    443                 bname,
    444                 encoding->u.converted_text,
    445                 info,
    446                 encoding->encodingID,
    447                 encoding2id ("UTF-8"),
    448                 &actualname);
    449 
    450         if ((ret) && (newname)) {   // Return new name
    451             *newname = actualname;
    452         }else{
    453             g_free (actualname);
    454         }
    455 
    456         return ret;
    457     }
    458 
    459 done:
    460     if (fsexam_errno != ERR_OK && !setting->pref->dry_run) {
    461         gchar *log_path;
    462 
    463         log_path = compose_path (fullpath, info, FALSE, FALSE);
    464         fsexam_log_puts (setting->log_info, log_path, NULL);
    465         if (setting->display_msg)
    466             setting->display_msg (log_path, fsexam_error_get_msg());
    467 
    468         g_free (log_path);
    469     }
    470 
    471 cleanup:
    472 
    473     if (setting->pref->auto_detect)
    474         setting->pref->encode_list = fsexam_encoding_remove_auto (
    475                 setting->pref->encode_list);
    476 
    477     return ret;
    478 }
    479 
    480 /*
    481  * Convert the name of files in archive/compress type file
    482  * And will handle flags
    483  */
    484 static gboolean
    485 compress_convert_filename (const gchar *fullpath,
    486         Compress_info *info,
    487         FSEXAM_setting *setting,
    488         gboolean restore)
    489 {
    490     gchar           *bname = NULL;
    491     gchar           *dname = NULL;
    492     gchar           *newname = NULL;
    493     gchar           *abs_path = NULL;
    494     struct stat     statbuf;
    495     gboolean        ret = FALSE;
    496     gboolean        need_free_newname = FALSE;
    497 
    498     if ((NULL == fullpath) || (NULL == info) || (NULL == setting))
    499         return FALSE;
    500 
    501     fsexam_errno = ERR_OK;
    502     bname = g_path_get_basename (fullpath);
    503     dname = g_path_get_dirname (fullpath);
    504 
    505     if (lstat (fullpath, &statbuf) == -1)
    506         fsexam_errno = ERR_FILE_NONEXIST;
    507     else if (!(S_ISREG(statbuf.st_mode))
    508             && !(S_ISDIR(statbuf.st_mode)) && !(S_ISLNK(statbuf.st_mode))){
    509         fsexam_errno = ERR_FILE_TYPE_NOT_SUPPORT;
    510     }else if ((! setting->pref->hidden) && (*bname == '.')){
    511         fsexam_errno = ERR_IGNORE_HIDDEN_FILE;
    512     }else if ((!setting->pref->remote)
    513             && (is_remote_file (setting->remote_path, fullpath))) {
    514         fsexam_errno = ERR_IGNORE_REMOTE_FILE;
    515     }
    516 
    517     if (fsexam_errno != ERR_OK) {
    518         gchar *log_path = compose_path (fullpath, info, FALSE, FALSE);
    519 
    520         fsexam_log_puts (setting->log_info, log_path, NULL);
    521         if (setting->display_msg)
    522             setting->display_msg (log_path, fsexam_error_get_msg());
    523         g_free (log_path);
    524 
    525         goto done;
    526     }
    527 
    528     if (restore && (strlen (fullpath) != info->temp_dir_len)) {
    529        ret = compress_restore_single_filename (setting,
    530                 fullpath,
    531                 dname,
    532                 bname,
    533                 info,
    534                 &newname);
    535     }else if (strlen (fullpath) != info->temp_dir_len) {
    536         if ((!setting->pref->force)
    537                 && (str_isutf8 (bname, DEFAULT_DETECTING_FLAG))) {
    538             gchar *log_path = compose_path (fullpath, info, FALSE, FALSE);
    539             fsexam_errno = ERR_NAME_UTF8_ALREADY;
    540 
    541             fsexam_log_puts (setting->log_info, log_path, NULL);
    542             if (setting->display_msg)
    543                 setting->display_msg (log_path, fsexam_error_get_msg());
    544             g_free (log_path);
    545         }else{
    546             ret = compress_convert_single_filename (setting,
    547                 fullpath,
    548                 dname,
    549                 bname,
    550                 info,
    551                 &newname);
    552         }
    553     }
    554 
    555     if (ret && newname != NULL) {
    556         abs_path = g_strdup_printf ("%s/%s", dname, newname);
    557         need_free_newname = TRUE;
    558     }else{
    559         abs_path = (gchar *)fullpath;
    560     }
    561 
    562     if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
    563         goto done;
    564 
    565     if (fsexam_special_is_compress (abs_path, NULL)){
    566         /* nested compress file */
    567         gchar   *log_path = compose_path (abs_path, info, FALSE, FALSE);
    568         gchar   *result_path = compose_path (abs_path, info, TRUE, FALSE);
    569         gchar   *search_path;
    570 
    571         if (restore) {
    572             search_path = g_filename_to_uri (info->search_path, NULL, NULL);
    573 
    574         }else{
    575             search_path = compose_path (abs_path, info, TRUE, TRUE);
    576         }
    577 
    578         Compress_info *new_info = compress_info_new (result_path,
    579                 search_path + URI_HEAD_LEN,
    580                 log_path);
    581 
    582         g_free (log_path);
    583         g_free (result_path);
    584         g_free (search_path);
    585 
    586         if (NULL == new_info) {
    587             goto done;
    588         }
    589 
    590         compress_common_convert (abs_path,
    591                 new_info,
    592                 setting,
    593                 restore,
    594                 TRUE);
    595 
    596         compress_info_free (new_info);
    597     } else if (setting->pref->recursive
    598             && S_ISDIR (statbuf.st_mode)) { /* Directory */
    599         const gchar *filename = NULL;
    600         gchar       *old_search_path = g_strdup (info->search_path);
    601         GDir        *dp = g_dir_open (abs_path, 0, NULL);
    602 
    603         if (dp == NULL)
    604             goto done;
    605 
    606         while ((filename = g_dir_read_name (dp)) != NULL) {
    607             gchar *childname = g_strdup_printf ("%s/%s", abs_path, filename);
    608 
    609             if (restore) {
    610                 gchar *tmp;
    611                 tmp = g_strdup_printf (
    612                         info->temp_dir_len == strlen (abs_path) ? "%s\t%s"
    613                                                                 : "%s/%s",
    614                         old_search_path,
    615                         filename);
    616                 g_free (info->search_path);
    617                 info->search_path = tmp;
    618             }
    619 
    620             compress_convert_filename (childname,
    621                     info,
    622                     setting,
    623                     restore);
    624 
    625             g_free (childname);
    626 
    627             if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
    628                 break;
    629         }
    630 
    631         g_free (old_search_path);
    632         g_dir_close (dp);
    633     }
    634 
    635 done:
    636     if (need_free_newname) {
    637         g_free (newname);
    638     }
    639 
    640     g_free (bname);
    641     g_free (dname);
    642 
    643     return ret;
    644 }
    645 
    646 /*
    647  * Modify file content on the disk
    648  */
    649 static gboolean
    650 compress_write_back_contents (FSEXAM_setting *setting,
    651         const gchar *fullpath,
    652         Compress_info *info,
    653         gchar *converted_contents,
    654         short fid,
    655         short tid)
    656 {
    657     gsize       length;
    658     gchar       *hist_path = NULL;
    659     gchar       *log_path = NULL;
    660     gboolean    same_serial = TRUE;
    661     gboolean    need_free = FALSE;
    662     gboolean    ret = FALSE;
    663     const gchar  *from_encoding = id2encoding (fid);
    664     const gchar  *to_encoding = id2encoding (tid);
    665 
    666     if (converted_contents == NULL) {
    667         gchar        *contents = NULL;
    668         gboolean    err;
    669 
    670         err = g_file_get_contents (fullpath, &contents, &length, NULL);
    671 
    672         if (err && !contents) {
    673             fsexam_errno = ERR_CANNOT_READ;
    674             goto done;
    675         } else if (length == 0) {
    676             fsexam_errno = ERR_EMPTY_FILE;
    677             goto done;
    678         }
    679 
    680         converted_contents = g_convert (contents,
    681                                         length,
    682                                         to_encoding,
    683                                         from_encoding,
    684                                         NULL,
    685                                         NULL,
    686                                         NULL);
    687 
    688         g_free (contents);
    689 
    690         if (converted_contents == NULL) {
    691             fsexam_errno = ERR_CANNOT_CONVERT;
    692             goto done;
    693         }
    694 
    695         need_free = TRUE;
    696     }
    697 
    698     length = strlen (converted_contents);
    699 
    700     /* Modify file contents on the disk */
    701     if (g_file_set_contents (fullpath, converted_contents, length, NULL)) {
    702         ret = TRUE;
    703         ++setting->succ_num;
    704     }else{
    705         fsexam_errno = ERR_CANNOT_WRITE;
    706         ++setting->fail_num;
    707         goto done;
    708     }
    709 
    710 
    711     if (setting->flags & FSEXAM_SETTING_FLAGS_UNDO) {
    712         goto done;
    713     }
    714 
    715     if (setting->flags & FSEXAM_SETTING_FLAGS_DIFF_SERIAL) {
    716         setting->flags &= ~FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
    717         same_serial = FALSE;
    718     }
    719 
    720     hist_path = compose_path (fullpath, info, TRUE, FALSE);
    721 
    722     fsexam_history_put (setting->hist_info,
    723                         ConvContentSpecial,
    724                         hist_path,
    725                         fid,
    726                         tid,
    727                         same_serial);
    728 
    729 done:
    730     log_path = compose_path (fullpath, info, FALSE, FALSE);
    731 
    732     /* log file */
    733     if (fsexam_errno == ERR_OK) {
    734         gchar   *msg = g_strdup_printf (_("[Content] %s -> %s"),
    735                         from_encoding,
    736                         to_encoding);
    737         fsexam_log_puts (setting->log_info, log_path, msg);
    738         if (setting->display_msg) {
    739             setting->display_msg (log_path, msg);
    740         }
    741         g_free (msg);
    742     }else{
    743         fsexam_log_puts (setting->log_info, log_path, NULL);
    744         if (setting->display_msg) {
    745             setting->display_msg (log_path, fsexam_error_get_msg ());
    746         }
    747     }
    748 
    749 
    750     g_free (hist_path);
    751     g_free (log_path);
    752 
    753     if (need_free)
    754         g_free (converted_contents);
    755 
    756     return ret;
    757 }
    758 
    759 /*
    760  * Restore single file's content.
    761  */
    762 static gboolean
    763 compress_restore_single_file_content (const gchar *fullpath,
    764         Compress_info *info,
    765         FSEXAM_setting *setting)
    766 {
    767     gboolean    ret = FALSE;
    768     gchar       *hist_path = NULL;
    769     gchar       *log_path = NULL;
    770     Hist_item   *item = NULL;
    771 
    772     ++setting->total_num;
    773     fsexam_errno = ERR_OK;
    774 
    775     hist_path = compose_path (fullpath, info, TRUE, TRUE);
    776     item = fsexam_history_search (setting->hist_info, hist_path, FALSE);
    777 
    778     if ((item == NULL) || (item->convtype != ConvContentSpecial)) {
    779         fsexam_errno = ERR_HIST_NO_ITEM;
    780         goto done;
    781     }
    782 
    783     ret = compress_write_back_contents (setting,
    784             fullpath,
    785             info,                   /* Compress info struct */
    786             NULL,                   /* contents pointer */
    787             item->to_encoding,
    788             item->from_encoding);
    789 
    790     goto cleanup;
    791 
    792 done:
    793     log_path = compose_path (fullpath, info, FALSE, FALSE);
    794 
    795     fsexam_log_puts (setting->log_info, log_path, NULL);
    796     if (setting->display_msg)
    797         setting->display_msg (log_path, fsexam_error_get_msg());
    798 
    799 cleanup:
    800     g_free (hist_path);
    801     g_free (log_path);
    802 
    803     return ret;
    804 }
    805 
    806 /*
    807  * Convert single one file's content. Don't care flags
    808  */
    809 static gboolean
    810 compress_convert_single_file_content (const gchar *fullpath,
    811         Compress_info *info,
    812         FSEXAM_setting *setting)
    813 {
    814     Score       score;
    815     Encoding    *encoding = NULL;
    816     gboolean    err;
    817     gchar       *sample_contents = NULL;
    818     gchar       *contents = NULL;
    819     gsize       length;
    820     gint        index = 0;
    821     gboolean    need_free_contents = FALSE;
    822     gboolean    ret = FALSE;
    823 
    824     fsexam_errno = ERR_OK;
    825     ++setting->total_num;
    826 
    827     if (! file_validate_for_contentconv (fullpath, setting)) {
    828         /* Don't need convert current file's content */
    829         goto done;
    830     }
    831 
    832     /* handle encoding auto detection, plain text need this */
    833     if (setting->pref->auto_detect) {
    834         GList *detected_encoding;
    835 
    836         detected_encoding = file_encoding_detect (fullpath,
    837                 DEFAULT_DETECTING_FLAG);
    838         setting->pref->encode_list = fsexam_encoding_add_auto (
    839                 setting->pref->encode_list,
    840                 detected_encoding);
    841         auto_encoding_free (detected_encoding);
    842     }
    843 
    844     if (! fsexam_is_plain_text (fullpath, setting)) {
    845         fsexam_errno = ERR_FILE_TYPE_NOT_SUPPORT;
    846         goto done;
    847     }
    848 
    849     /* plain text now */
    850     err = g_file_get_contents (fullpath, &contents, &length, NULL);
    851     if (err && !contents) {
    852         fsexam_errno = ERR_CANNOT_READ;
    853         goto done;
    854     } else if (length == 0) {
    855         fsexam_errno = ERR_EMPTY_FILE;
    856         goto done;
    857     }
    858 
    859     sample_contents = get_sample_text (contents, &length);
    860     score = fsexam_encoding_decode (setting->pref->encode_list,
    861                         ConvContent,
    862                         sample_contents,
    863                         length,
    864                         setting->pref->force);
    865 
    866     g_free (contents);
    867     g_free (sample_contents);
    868 
    869     need_free_contents = TRUE;      /* free contents in encoding_list */
    870 
    871     if (setting->pref->dry_run) {
    872         /* Don't support dry run for special file */
    873     }else{
    874         if ((score == FAIL) || (score == ORIGINAL)){
    875             fsexam_errno = (score == FAIL) ? ERR_NO_PROPER_ENCODING
    876                                            : ERR_CONTENT_UTF8_ALREADY;
    877             goto done;
    878         }
    879 
    880         if (setting->gold_index != -1) {
    881             index = setting->gold_index;
    882         } else if (setting->pref->auto_conversion) {
    883             index = fsexam_encoding_get_first_index (
    884                             setting->pref->encode_list);
    885         } else {
    886             index = setting->get_index (setting->pref->encode_list, FALSE);
    887         }
    888 
    889         if (index == -1) {
    890             goto cleanup;
    891         }
    892 
    893         encoding = (Encoding *)g_list_nth_data (setting->pref->encode_list,
    894                                                 index);
    895         if (NULL == encoding){
    896             fsexam_errno = ERR_ENCODING_INDEX_INVALID;
    897             goto done;
    898         }
    899 
    900         ret = compress_write_back_contents (setting,
    901                     fullpath,
    902                     info,
    903                     NULL,       /* contents pointer */
    904                     encoding->encodingID,
    905                     encoding2id ("UTF-8"));
    906     }
    907 
    908 done:
    909     if (fsexam_errno != ERR_OK && !setting->pref->dry_run) {
    910         gchar *log_path = compose_path (fullpath, info, FALSE, FALSE);
    911 
    912         fsexam_log_puts (setting->log_info, log_path, NULL);
    913         if (setting->display_msg)
    914             setting->display_msg (log_path, fsexam_error_get_msg());
    915 
    916         g_free (log_path);
    917     }
    918 
    919 cleanup:
    920     if (setting->pref->auto_detect)
    921         setting->pref->encode_list = fsexam_encoding_remove_auto (
    922                                             setting->pref->encode_list);
    923 
    924     if (need_free_contents)
    925         fsexam_encoding_cleanup_content (setting->pref->encode_list);
    926 
    927     return ret;
    928 }
    929 
    930 /*
    931  *  Convert the content of unarchive/uncompress files.
    932  *  don't handle symlink for special type file.
    933  */
    934 static gboolean
    935 compress_convert_content (const gchar *fullpath,
    936         Compress_info *info,
    937         FSEXAM_setting *setting,
    938         gboolean restore)
    939 {
    940     gboolean ret = FALSE;
    941     gchar   *abs_path = get_abs_path (fullpath);
    942     struct  stat statbuf;
    943 
    944     if ((abs_path == NULL) || (setting == NULL) || (info == NULL))
    945         return FALSE;
    946 
    947     if (lstat (abs_path, &statbuf) == -1) {
    948         fsexam_errno = ERR_CANNOT_OPEN;
    949         goto done;
    950     }
    951 
    952     if (fsexam_special_is_compress (abs_path, NULL)){
    953         /* nested compress file */
    954         gchar   *search_path = compose_path (abs_path, info, TRUE, TRUE);
    955         gchar   *result_path = compose_path (abs_path, info, TRUE, FALSE);
    956         gchar   *log_path = compose_path (abs_path, info, FALSE, FALSE);
    957         Compress_info *new_info = compress_info_new (result_path,
    958                                                     search_path,
    959                                                     log_path);
    960         if (NULL == new_info) {
    961             goto done;
    962         }
    963 
    964         compress_common_convert (abs_path,
    965                 new_info,
    966                 setting,
    967                 restore,
    968                 FALSE);
    969 
    970         compress_info_free (new_info);
    971     }else if (S_ISREG (statbuf.st_mode)) {  /* regular file */
    972         if (restore) {
    973             compress_restore_single_file_content (abs_path,
    974                     info,
    975                     setting);
    976         }else{
    977             compress_convert_single_file_content (abs_path,
    978                     info,
    979                     setting);
    980         }
    981     } else if (setting->pref->recursive
    982             && S_ISDIR (statbuf.st_mode)) { /* Directory */
    983         const gchar *filename = NULL;
    984         GDir        *dp = g_dir_open (abs_path, 0, NULL);
    985 
    986         if (dp == NULL)
    987             goto done;
    988 
    989         while ((filename = g_dir_read_name (dp)) != NULL) {
    990             gchar *childname = g_strdup_printf ("%s/%s", abs_path, filename);
    991 
    992             compress_convert_content (childname,
    993                     info,
    994                     setting,
    995                     restore);
    996 
    997             g_free (childname);
    998 
    999             if (setting->flags && FSEXAM_SETTING_FLAGS_STOP)
   1000                 break;
   1001         }
   1002 
   1003         g_dir_close (dp);
   1004     }
   1005 
   1006 done:
   1007     g_free (abs_path);
   1008 
   1009     return ret;
   1010 }
   1011 
   1012 /*
   1013  * Whether give file is supported archive/compress file or not
   1014  */
   1015 static gboolean
   1016 fsexam_special_is_compress (const gchar *filename, Compress_Type *type)
   1017 {
   1018     int i;
   1019 
   1020     if (NULL == filename) {
   1021         return FALSE;
   1022     }
   1023 
   1024     for (i = 0; i < sizeof (compress_pair)/sizeof (compress_pair[0]); i++) {
   1025         if (g_str_has_suffix (filename, compress_pair[i][0])) {
   1026             if (type != NULL)
   1027                 *type = (Compress_Type) i;
   1028 
   1029             return TRUE;
   1030         }
   1031     }
   1032 
   1033     return FALSE;
   1034 }
   1035 
   1036 static gboolean
   1037 get_iso8859_locale (FSEXAM_setting *setting)
   1038 {
   1039     static gboolean first = TRUE;
   1040 
   1041     if (first) {
   1042         char  *old_locale = setlocale (LC_ALL, NULL);
   1043 
   1044         if ((setlocale (LC_ALL, "en_US.ISO8859-1")) != NULL) {
   1045             iso8859_locale = "en_US.ISO8859-1";
   1046         }else if ((setlocale (LC_ALL, "fr_FR.ISO8859-1")) != NULL) {
   1047             iso8859_locale = "fr_FR.ISO8859-1";
   1048         }else if ((setlocale (LC_ALL, "de_DE.ISO8859-1")) != NULL) {
   1049             iso8859_locale = "de_DE.ISO8859-1";
   1050         }else if ((setlocale (LC_ALL, "es_ES.ISO8859-1")) != NULL) {
   1051             iso8859_locale = "es_ES.ISO8859-1";
   1052         }else if ((setlocale (LC_ALL, "it_IT.ISO8859-1")) != NULL) {
   1053             iso8859_locale = "it_IT.ISO8859-1";
   1054         }else if ((setlocale (LC_ALL, "sv_SE.ISO8859-1")) != NULL) {
   1055             iso8859_locale = "sv_SE.ISO8859-1";
   1056         }else{
   1057             iso8859_locale = NULL;
   1058         }
   1059 
   1060         /* reset locale */
   1061         if (old_locale != NULL)
   1062             setlocale (LC_ALL, old_locale);
   1063 
   1064         first = FALSE;
   1065     }
   1066 
   1067     if (iso8859_locale == NULL && setting->display_msg)
   1068         setting->display_msg (NULL, _("Can not run tar command due to lack of iso8859-1 locale, please see fsexam(1) man page for more info."));
   1069 
   1070     return (iso8859_locale != NULL);
   1071 }
   1072 
   1073 static gboolean
   1074 run_cmd (gchar **argv, gchar **envp, FSEXAM_setting *setting)
   1075 {
   1076     FILE *fp = NULL;
   1077     gchar tmp[200];
   1078     GError *error = NULL;
   1079     gboolean ret = FALSE;
   1080     gint child_stderr;
   1081 
   1082     if (! g_spawn_async_with_pipes (NULL, argv, envp, 0,
   1083                 NULL, NULL, NULL, NULL,
   1084                 NULL,
   1085                 &child_stderr,
   1086                 &error)) {
   1087         setting->display_msg (NULL, error->message);
   1088         g_error_free (error);
   1089         goto DONE;
   1090     }
   1091 
   1092     fp = fdopen (child_stderr, "r");
   1093     if (fp == NULL) {
   1094         goto DONE;
   1095     }
   1096 
   1097     if (fgets (tmp, sizeof (tmp), fp) != NULL) {
   1098         setting->display_msg (NULL, tmp);
   1099         goto DONE;
   1100     }
   1101 
   1102     ret = TRUE;
   1103 
   1104 DONE:
   1105     if (fp != NULL)
   1106         fclose (fp);
   1107 
   1108     return ret;
   1109 }
   1110 
   1111 static gboolean
   1112 uncompress_pre (const gchar *fullpath, const gchar *tmpdir,
   1113         FSEXAM_setting *setting)
   1114 {
   1115     gchar **argv = NULL;
   1116     gboolean ret = FALSE;
   1117 
   1118     if (! get_iso8859_locale (setting)) {
   1119        return FALSE;
   1120     }
   1121 
   1122     /* cp target file into current directory */
   1123     argv = g_malloc (sizeof (gchar **) * 4);
   1124     argv[0] = "/usr/bin/cp";
   1125     argv[1] = (gchar *)fullpath;
   1126     argv[2] = (gchar *)tmpdir;
   1127     argv[3] = NULL;
   1128 
   1129     if (! run_cmd (argv, NULL, setting))
   1130         goto DONE;
   1131 
   1132     ret = TRUE;
   1133 
   1134 DONE:
   1135     g_free (argv);
   1136 
   1137     return ret;
   1138 }
   1139 
   1140 static gboolean
   1141 delete_files (const gchar *filename, FSEXAM_setting *setting)
   1142 {
   1143     gchar **argv = NULL;
   1144     gboolean ret = FALSE;
   1145 
   1146     /* rm target file into current directory */
   1147     argv = g_malloc (sizeof (gchar **) * 4);
   1148     argv[0] = "/usr/bin/rm";
   1149     argv[1] = "-rf";
   1150     argv[2] = (gchar *)filename;
   1151     argv[3] = NULL;
   1152 
   1153     if (! run_cmd (argv, NULL, setting))
   1154         goto DONE;
   1155 
   1156     ret = TRUE;
   1157 
   1158 DONE:
   1159     g_free (argv);
   1160 
   1161     return ret;
   1162 }
   1163 
   1164 static gboolean
   1165 uncompress_post (const gchar *filename, FSEXAM_setting *setting)
   1166 {
   1167    if (! delete_files (filename, setting))
   1168        return FALSE;
   1169 
   1170    return TRUE;
   1171 }
   1172 
   1173 static gboolean
   1174 uncompress_bz2 (const gchar *fullpath, const gchar *tmpdir,
   1175         FSEXAM_setting *setting)
   1176 {
   1177     gchar *bname = NULL;
   1178     gchar **argv = NULL;
   1179     gchar **envp = NULL;
   1180     gboolean ret = FALSE;
   1181 
   1182     if (! uncompress_pre (fullpath, tmpdir, setting)) {
   1183         return FALSE;
   1184     }
   1185 
   1186     bname = g_path_get_basename (fullpath);
   1187 
   1188     argv = g_malloc (sizeof (gchar **) * 4);
   1189     argv[0] = "/usr/bin/bunzip2";
   1190     argv[1] = bname;
   1191     argv[2] = NULL;
   1192 
   1193     /* bunzip2 */
   1194     if (! run_cmd (argv, NULL, setting)) {
   1195         goto DONE;
   1196     }
   1197 
   1198     /* untar */
   1199     *(bname + strlen (bname) - 4) = '\0';  /* 4 is strlen(".bz2") */
   1200     argv[0] = TARCMD;
   1201     argv[1] = "-xf";
   1202     argv[2] = bname;
   1203     argv[3] = NULL;
   1204 
   1205     envp = g_malloc (sizeof (gchar **) * 2);
   1206     envp[0] = iso8859_locale;
   1207     envp[1] = NULL;
   1208 
   1209     if (! run_cmd (argv, envp, setting))
   1210         goto DONE;
   1211 
   1212     /* delete bname */
   1213     if (! uncompress_post (bname, setting))
   1214         goto DONE;
   1215 
   1216 
   1217     ret = TRUE;
   1218 
   1219 DONE:
   1220     g_free (bname);
   1221     g_free (argv);
   1222     g_free (envp);
   1223 
   1224     return ret;
   1225 }
   1226 
   1227 static gboolean
   1228 uncompress_gz (const gchar *fullpath, const gchar *tmpdir,
   1229         FSEXAM_setting *setting)
   1230 {
   1231     gchar *bname = NULL;
   1232     gchar **argv = NULL;
   1233     gchar **envp = NULL;
   1234     gboolean ret = FALSE;
   1235 
   1236     if (! uncompress_pre (fullpath, tmpdir, setting)) {
   1237         return FALSE;
   1238     }
   1239 
   1240     bname = g_path_get_basename (fullpath);
   1241 
   1242     argv = g_malloc (sizeof (gchar **) * 4);
   1243     argv[0] = "/usr/bin/gunzip";
   1244     argv[1] = bname;
   1245     argv[2] = NULL;
   1246 
   1247     /* bunzip2 */
   1248     if (! run_cmd (argv, NULL, setting)) {
   1249         goto DONE;
   1250     }
   1251 
   1252     /* untar */
   1253     *(bname + strlen (bname) - 3) = '\0';  /* 3 is strlen(".gz") */
   1254     argv[0] = TARCMD;
   1255     argv[1] = "-xf";
   1256     argv[2] = bname;
   1257     argv[3] = NULL;
   1258 
   1259     envp = g_malloc (sizeof (gchar **) * 2);
   1260     envp[0] = iso8859_locale;
   1261     envp[1] = NULL;
   1262 
   1263     if (! run_cmd (argv, envp, setting))
   1264         goto DONE;
   1265 
   1266     /* delete bname */
   1267     if (! uncompress_post (bname, setting))
   1268         goto DONE;
   1269 
   1270 
   1271     ret = TRUE;
   1272 
   1273 DONE:
   1274     g_free (bname);
   1275     g_free (argv);
   1276     g_free (envp);
   1277 
   1278     return ret;
   1279 }
   1280 
   1281 static gboolean
   1282 uncompress_tar (const gchar *fullpath, FSEXAM_setting *setting)
   1283 {
   1284     gchar **argv = NULL;
   1285     gchar **envp = NULL;
   1286     gboolean ret = FALSE;
   1287 
   1288     if (! get_iso8859_locale (setting))
   1289         return FALSE;
   1290 
   1291     argv = g_malloc (sizeof (gchar **) * 4);
   1292     argv[0] = TARCMD;
   1293     argv[1] = "-xf";
   1294     argv[2] = (gchar *)fullpath;
   1295     argv[3] = NULL;
   1296 
   1297     envp = g_malloc (sizeof (gchar **) * 2);
   1298     envp[0] = iso8859_locale;
   1299     envp[1] = NULL;
   1300 
   1301     if (! run_cmd (argv, envp, setting))
   1302         goto DONE;
   1303 
   1304     ret = TRUE;
   1305 
   1306 DONE:
   1307     g_free (argv);
   1308     g_free (envp);
   1309 
   1310     return ret;
   1311 }
   1312 
   1313 static gboolean
   1314 uncompress_unzip (const gchar *fullpath, FSEXAM_setting *setting)
   1315 {
   1316     gchar **argv = NULL;
   1317     gboolean ret = FALSE;
   1318 
   1319     argv = g_malloc (sizeof (gchar **) * 4);
   1320     argv[0] = "/usr/bin/unzip";
   1321     argv[1] = "-q";
   1322     argv[2] = (gchar *)fullpath;
   1323     argv[3] = NULL;
   1324 
   1325     if (! run_cmd (argv, NULL, setting))
   1326         goto DONE;
   1327 
   1328     ret = TRUE;
   1329 
   1330 DONE:
   1331     g_free (argv);
   1332 
   1333     return ret;
   1334 }
   1335 
   1336 static gboolean
   1337 uncompress_Z (const gchar *fullpath, const gchar *tmpdir,
   1338         FSEXAM_setting *setting)
   1339 {
   1340     gchar *bname = NULL;
   1341     gchar **argv = NULL;
   1342     gchar **envp = NULL;
   1343     gboolean ret = FALSE;
   1344 
   1345     if (! uncompress_pre (fullpath, tmpdir, setting)) {
   1346         return FALSE;
   1347     }
   1348 
   1349     bname = g_path_get_basename (fullpath);
   1350 
   1351     argv = g_malloc (sizeof (gchar **) * 4);
   1352     argv[0] = "/usr/bin/uncompress";
   1353     argv[1] = "-c";
   1354     argv[2] = bname;
   1355     argv[3] = NULL;
   1356 
   1357     /* bunzip2 */
   1358     if (! run_cmd (argv, NULL, setting)) {
   1359         goto DONE;
   1360     }
   1361 
   1362     /* untar */
   1363     *(bname + strlen (bname) - 2) = '\0';  /* 2 is strlen(".Z") */
   1364     argv[0] = TARCMD;
   1365     argv[1] = "-xf";
   1366     argv[2] = bname;
   1367     argv[3] = NULL;
   1368 
   1369     envp = g_malloc (sizeof (gchar **) * 2);
   1370     envp[0] = iso8859_locale;
   1371     envp[1] = NULL;
   1372 
   1373     if (! run_cmd (argv, envp, setting))
   1374         goto DONE;
   1375 
   1376     /* delete bname */
   1377     if (! uncompress_post (bname, setting))
   1378         goto DONE;
   1379 
   1380 
   1381     ret = TRUE;
   1382 
   1383 DONE:
   1384     g_free (bname);
   1385     g_free (argv);
   1386     g_free (envp);
   1387 
   1388     return ret;
   1389 }
   1390 
   1391 /*
   1392  * Uncompress .bz2, .gz, .zip, .Z
   1393  */
   1394 static gboolean
   1395 fsexam_uncompress (const gchar *fullpath, const gchar *tmpdir,
   1396         Compress_Type type, FSEXAM_setting *setting)
   1397 {
   1398     /* uncompress */
   1399     if (type == COMP_TARGZ) {
   1400         return uncompress_gz (fullpath, tmpdir, setting);
   1401     } else if (type == COMP_TARBZ2) {
   1402         return uncompress_bz2 (fullpath, tmpdir, setting);
   1403     } else if (type == COMP_TAR) {
   1404         return uncompress_tar (fullpath, setting);
   1405     } else if (type == COMP_ZIP) {
   1406         return uncompress_unzip (fullpath, setting);
   1407     } else if (type == COMP_Z) {
   1408         return uncompress_Z (fullpath, tmpdir, setting);
   1409     } else {
   1410          if (setting->display_msg)
   1411              setting->display_msg (fullpath,
   1412                      _("Don't support this file type"));
   1413     }
   1414 
   1415     return FALSE;
   1416 }
   1417 
   1418 static gboolean
   1419 compress_bz2 (const gchar *target_name, FSEXAM_setting *setting)
   1420 {
   1421     gchar **argv = NULL;
   1422     gboolean ret = FALSE;
   1423 
   1424     argv = g_malloc (sizeof (gchar **) * 5);
   1425     argv[0] = TARCMD;
   1426     argv[1] = "-cjf";
   1427     argv[2] = (gchar *)target_name;
   1428     argv[3] = ".";
   1429     argv[4] = NULL;
   1430 
   1431     if (! run_cmd (argv, NULL, setting)) {
   1432         goto DONE;
   1433     }
   1434 
   1435     ret = TRUE;
   1436 
   1437 DONE:
   1438     g_free (argv);
   1439 
   1440     return ret;
   1441 }
   1442 
   1443 static gboolean
   1444 compress_tar (const gchar *target_name, FSEXAM_setting *setting)
   1445 {
   1446     gchar **argv = NULL;
   1447     gboolean ret = FALSE;
   1448 
   1449     argv = g_malloc (sizeof (gchar **) * 5);
   1450     argv[0] = TARCMD;
   1451     argv[1] = "-cf";
   1452     argv[2] = (gchar *)target_name;
   1453     argv[3] = ".";
   1454     argv[4] = NULL;
   1455 
   1456     if (! run_cmd (argv, NULL, setting)) {
   1457         goto DONE;
   1458     }
   1459 
   1460     ret = TRUE;
   1461 
   1462 DONE:
   1463     g_free (argv);
   1464 
   1465     return ret;
   1466 }
   1467 
   1468 static gboolean
   1469 compress_Z (const gchar *target_name, FSEXAM_setting *setting)
   1470 {
   1471     gchar **argv = NULL;
   1472     gboolean ret = FALSE;
   1473 
   1474     argv = g_malloc (sizeof (gchar **) * 5);
   1475     argv[0] = TARCMD;
   1476     argv[1] = "-cZf";
   1477     argv[2] = (gchar *)target_name;
   1478     argv[3] = ".";
   1479     argv[4] = NULL;
   1480 
   1481     if (! run_cmd (argv, NULL, setting)) {
   1482         goto DONE;
   1483     }
   1484 
   1485     ret = TRUE;
   1486 
   1487 DONE:
   1488     g_free (argv);
   1489 
   1490     return ret;
   1491 }
   1492 
   1493 /*
   1494  * zip in the current directory, then move to target dir.
   1495  * Otherwise zip(1) will append the new files into old file
   1496  */
   1497 static gboolean
   1498 compress_zip (const gchar *target_name, const gchar *tmpdir,
   1499         FSEXAM_setting *setting)
   1500 {
   1501     gchar **argv = NULL;
   1502     gboolean ret = FALSE;
   1503     gchar *bname = NULL;
   1504     gchar *tmpname = NULL;
   1505 
   1506     bname = g_path_get_basename (target_name);
   1507     tmpname = g_strdup_printf ("%s/%s", tmpdir, bname);
   1508 
   1509     argv = g_malloc (sizeof (gchar **) * 5);
   1510     argv[0] = "/usr/bin/zip";
   1511     argv[1] = "-qr";
   1512     argv[2] = tmpname;
   1513     argv[3] = ".";
   1514     argv[4] = NULL;
   1515 
   1516     if (! run_cmd (argv, NULL, setting)) {
   1517         goto DONE;
   1518     }
   1519 
   1520     argv[0] = "/usr/bin/cp";
   1521     argv[1] = tmpname;
   1522     argv[2] = (gchar *)target_name;
   1523     argv[3] = NULL;
   1524 
   1525     if (! run_cmd (argv, NULL, setting))
   1526         goto DONE;
   1527 
   1528     ret = TRUE;
   1529 
   1530 DONE:
   1531     g_free (argv);
   1532     g_free (bname);
   1533     g_free (tmpname);
   1534 
   1535     return ret;
   1536 }
   1537 
   1538 
   1539 static gboolean
   1540 compress_gz (const gchar *target_name, FSEXAM_setting *setting)
   1541 {
   1542     gchar **argv = NULL;
   1543     gboolean ret = FALSE;
   1544 
   1545     argv = g_malloc (sizeof (gchar **) * 5);
   1546     argv[0] = TARCMD;
   1547     argv[1] = "-czf";
   1548     argv[2] = (gchar *)target_name;
   1549     argv[3] = ".";
   1550     argv[4] = NULL;
   1551 
   1552     if (! run_cmd (argv, NULL, setting)) {
   1553         goto DONE;
   1554     }
   1555 
   1556     ret = TRUE;
   1557 
   1558 DONE:
   1559     g_free (argv);
   1560 
   1561     return ret;
   1562 }
   1563 
   1564 
   1565 /*
   1566  * Compress .bz2, .gz, .zip, .Z
   1567  *  dname: the directory name which will contain the result file
   1568  *  bname: the result filename, such as *.tar.bz2
   1569  */
   1570 static gboolean
   1571 fsexam_compress (const gchar *target_name, const gchar *tmpdir,
   1572         Compress_Type type, FSEXAM_setting *setting)
   1573 {
   1574     /* uncompress */
   1575     if (type == COMP_TARGZ) {
   1576         return compress_gz (target_name, setting);
   1577     } else if (type == COMP_TARBZ2) {
   1578         return compress_bz2 (target_name, setting);
   1579     } else if (type == COMP_TAR) {
   1580         return compress_tar (target_name, setting);
   1581     } else if (type == COMP_ZIP) {
   1582         return compress_zip (target_name, tmpdir, setting);
   1583     } else if (type == COMP_Z) {
   1584         return compress_Z (target_name, setting);
   1585     } else {
   1586          if (setting->display_msg)
   1587              setting->display_msg (target_name,
   1588                      _("Don't support this file type"));
   1589     }
   1590 
   1591     return FALSE;
   1592 }
   1593 
   1594 /*
   1595  * Description:
   1596  *      Unarchive/Uncompress file, execute name or content conversion, then
   1597  *      Rearchive/Recompress and replace the original file.
   1598  *
   1599  * Parameter:
   1600  *      fullpath: the full path of compress file, such as /tmp/a.tar
   1601  *      info:     Path needed by compress conversion
   1602  */
   1603 static gboolean
   1604 compress_common_convert (const gchar *fullpath,
   1605         Compress_info *info,
   1606         FSEXAM_setting *setting,
   1607         gboolean restore,
   1608         gboolean nameconvert)
   1609 {
   1610     Compress_Type   type;
   1611     gchar    oldcwd[PATH_MAX];
   1612     gchar    *bname = NULL;
   1613     gchar    *dname = NULL;
   1614     gchar    *tempdir = NULL;       /* temparay directory */
   1615     gchar    *template = NULL;      /* template for temp dir */
   1616     gchar    *compress_cmd = NULL;
   1617 
   1618     if (! fsexam_special_is_compress (fullpath, &type)) {
   1619         return FALSE;
   1620     }
   1621 
   1622     /*
   1623      * cd to temparary directory at the same directory
   1624      * with original file  and then uncompress.
   1625      */
   1626     if (getcwd (oldcwd, PATH_MAX) == NULL) {
   1627         fsexam_errno = ERR_GET_CWD;
   1628         goto done;
   1629     }
   1630 
   1631     bname = g_path_get_basename (fullpath);
   1632     dname = g_path_get_dirname (fullpath);
   1633 
   1634     template = g_strdup_printf ("%sXXXXXX", fullpath);
   1635     tempdir = mkdtemp (template);
   1636 
   1637     if (tempdir == NULL) {
   1638         if (fsexam_debug () & FSEXAM_DBG_ARCHIVE)
   1639             g_print ("mkdtemp return NULL\n");
   1640 
   1641         fsexam_errno = ERR_NO_RIGHTS;
   1642         goto done;
   1643     }
   1644 
   1645     if (fsexam_debug () & FSEXAM_DBG_ARCHIVE)
   1646         g_print ("Succeed to create tempdir %s\n", tempdir);
   1647 
   1648     if (chdir (tempdir)) {          /* uncompress file into temp dir */
   1649         fsexam_errno = ERR_NO_RIGHTS;
   1650         goto done;
   1651     }
   1652 
   1653     if (! fsexam_uncompress (fullpath, tempdir, type, setting)) {
   1654         if (fsexam_debug () & FSEXAM_DBG_ARCHIVE) {
   1655             g_print ("Error when uncompress %s to %s\n", fullpath, tempdir);
   1656         }
   1657         /* chdir to orig dir at error */
   1658         chdir (oldcwd);
   1659         goto done;
   1660     }
   1661 
   1662     if (fsexam_debug () & FSEXAM_DBG_ARCHIVE)
   1663         g_print ("Succeed to uncompress %s to %s\n", fullpath, tempdir);
   1664 
   1665     /*
   1666      * Before converting, ensure the tempdir is not empty.
   1667      * If it is empty, this means that the archive file
   1668      * may have absolute path, this will dangerous. Or
   1669      * we met with unknow error.
   1670      */
   1671     if (rmdir (tempdir) == 0) {
   1672         if (fsexam_debug () & FSEXAM_DBG_ARCHIVE) {
   1673             g_print ("Error: archive use absolute path or met unknow error\n");
   1674         }
   1675 
   1676         if (setting->display_msg)
   1677             setting->display_msg (NULL,
   1678                     _("The archive file is empty or has absolute path, so can not convert files in this archive file"));
   1679         chdir (oldcwd);
   1680         goto done;
   1681     }
   1682 
   1683     info->temp_dir_len = strlen (tempdir);
   1684 
   1685     if (nameconvert) {      /* name conversion */
   1686         compress_convert_filename (tempdir,     /* contain unarchived files */
   1687                 info,
   1688                 setting,
   1689                 restore);
   1690     } else {                /* content conversion */
   1691         compress_convert_content (tempdir,      /* contain unarchived files */
   1692                 info,
   1693                 setting,
   1694                 restore);
   1695     }
   1696 
   1697     /* recompress */
   1698     if (! fsexam_compress (fullpath, tempdir, type, setting)) {
   1699         /* chdir to orig dir at error */
   1700         if (fsexam_debug () & FSEXAM_DBG_ARCHIVE) {
   1701             g_print ("Error when recompress %s to %s\n", tempdir, fullpath);
   1702         }
   1703     }
   1704 
   1705     if (fsexam_debug () & FSEXAM_DBG_ARCHIVE)
   1706         g_print ("Succeed to recompress %s to %s\n", tempdir, fullpath);
   1707 
   1708     chdir (oldcwd);
   1709     if (! delete_files (tempdir, setting)) {
   1710         if (fsexam_debug () & FSEXAM_DBG_ARCHIVE) {
   1711             g_print ("Error when delete tempdir: %s\n", tempdir);
   1712         }
   1713     }
   1714 
   1715     if (fsexam_debug () & FSEXAM_DBG_ARCHIVE)
   1716         g_print ("Succeed to delete tempdir %s\n", tempdir);
   1717 
   1718 done:
   1719     g_free (dname);
   1720     g_free (bname);
   1721     g_free (template);
   1722 
   1723     return TRUE;
   1724 }
   1725 
   1726 /* content conversion */
   1727 static gboolean
   1728 fsexam_compress_convert_content (const gchar *fullpath,
   1729         FSEXAM_setting *setting,
   1730         gboolean restore)
   1731 {
   1732     Compress_info *info;
   1733     gboolean      ret;
   1734 
   1735     info = compress_info_new (fullpath, NULL, NULL);
   1736 
   1737     if (info == NULL)
   1738         return FALSE;
   1739 
   1740     ret = compress_common_convert (fullpath,   /* abs path of archive file */
   1741                 info,
   1742                 setting,
   1743                 restore,    /* Convert or Restore */
   1744                 FALSE);     /* Content conversion instead of name conversion */
   1745 
   1746     compress_info_free (info);
   1747 
   1748     return ret;
   1749 }
   1750 
   1751 
   1752 
   1753 
   1754 /* ----------- For content conversion --------------------------- */
   1755 
   1756 gboolean
   1757 fsexam_special_is_special_for_content (const gchar *fullpath,
   1758         FSEXAM_setting *setting)
   1759 {
   1760     return fsexam_special_is_compress (fullpath, NULL);
   1761 }
   1762 
   1763 /*
   1764  * Iterate every special file handling module. This assumes that no two special
   1765  * module will handle the same one file.  Return True if file has been
   1766  * proccessed by one special module; otherwise return False.
   1767  */
   1768 gboolean
   1769 fsexam_special_convert_content (const gchar *fullpath,
   1770         FSEXAM_setting *setting,
   1771         gboolean restore)
   1772 {
   1773     g_return_val_if_fail ((fullpath != NULL) && (setting != NULL), FALSE);
   1774 
   1775     gboolean        ret;
   1776 
   1777     if (fsexam_compress_convert_content (fullpath, setting, restore)) {
   1778         ret = TRUE;
   1779     }else{
   1780         ret = FALSE;
   1781     }
   1782 
   1783     return ret;
   1784 }
   1785 
   1786 
   1787 /* ------------- For name convert ------------------- */
   1788 
   1789 /*
   1790  *  If fullpath is supported archive or compress file, then
   1791  *  return TRUE, otherwise return FALSE.
   1792  */
   1793 gboolean
   1794 fsexam_special_is_special_for_name (const gchar *fullpath,
   1795                                     FSEXAM_setting *setting)
   1796 {
   1797     return fsexam_special_is_compress (fullpath, NULL);
   1798 }
   1799 
   1800 /*===================================================================
   1801  *  Description:
   1802  *      Convert or Restore the name of special type file.
   1803  *
   1804  *  Parameters:
   1805  *      setting: Contain Total setting/preference information
   1806  *      fullpath: Absolute path or archive or compress file, such as
   1807  *                /tmp/test/test.tar.gz
   1808  *      hist_search_path: Use the oldname(before restore) as history search
   1809  *               path. This is only used for restore. This can ensure path
   1810  *               in history is consistent with path in disk.
   1811  *      restore: convert name or restore name
   1812  *
   1813  *  Return value:
   1814  *=====================================================================*/
   1815 gboolean
   1816 fsexam_compress_convert_name (FSEXAM_setting *setting,
   1817                             const gchar *fullpath,
   1818                             const gchar *hist_search_path,
   1819                             gboolean restore)
   1820 {
   1821     gboolean      ret;
   1822     Compress_info *info;
   1823     gchar         *search_path;
   1824 
   1825     /*
   1826      * hist_search_path is passed by caller, and it is different when
   1827      * restore is performed. When restore, search_path is the direct
   1828      * path used to search in history, don't need compose
   1829      */
   1830     search_path = g_filename_to_uri (hist_search_path, NULL, NULL);
   1831     info = compress_info_new (fullpath, search_path +URI_HEAD_LEN, fullpath);
   1832 
   1833     if (NULL == info) {
   1834         return FALSE;
   1835     }
   1836 
   1837     ret = compress_common_convert (fullpath,   /* abs path of archive file */
   1838                 info,
   1839                 setting,
   1840                 restore,            /* Convert or Restore */
   1841                 TRUE);              /* Convert Name instead of content */
   1842 
   1843     compress_info_free (info);
   1844     g_free (search_path);
   1845 
   1846     return ret;
   1847 }
   1848