src/cmd/fsexam/src/fsexam-convname.c
changeset 147 8c4ef02c14b8
equal deleted inserted replaced
146:841e634f8d60 147:8c4ef02c14b8
       
     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 
       
    27 #ifdef HAVE_CONFIG_H
       
    28 #include <config.h>
       
    29 #endif
       
    30 
       
    31 #include <strings.h>
       
    32 #include <unistd.h>
       
    33 #include <string.h>
       
    34 #include <stdlib.h>
       
    35 #include <dirent.h>
       
    36 #include <libgen.h>
       
    37 #include <unistd.h>
       
    38 
       
    39 #include "fsexam-header.h"
       
    40 #include "fsexam-convcontent.h"
       
    41 
       
    42 /* Append SUFFIX when has duplicated name */
       
    43 #define SUFFIX  "utf-8"
       
    44 
       
    45 static gboolean write_to_disk (FSEXAM_setting *,
       
    46                                const gchar *path,
       
    47                                const gchar *origname,
       
    48                                const gchar *utf8name,
       
    49                                short from_encoding,
       
    50                                short to_encoding,
       
    51                                gchar **actualname);
       
    52 
       
    53 static gboolean real_convert (FSEXAM_setting *setting, 
       
    54                               const gchar *dname, 
       
    55                               const gchar *bname, 
       
    56                               gchar **newname);
       
    57 static gboolean convert_symlink_target_node (
       
    58                               GNode *node, 
       
    59                               FSEXAM_setting *setting, 
       
    60                               gboolean restore);
       
    61 static gboolean wrapped_convert_node (GNode *node, FSEXAM_setting *setting);
       
    62 static gboolean wrapped_restore_convert_node (GNode *node, gpointer data);
       
    63 static gboolean convert_node (GNode *node, gpointer data);
       
    64 static gboolean dryrun_convert_node (GNode *node, gpointer data);
       
    65 static gboolean restore_convert_node (GNode *node, gpointer data);
       
    66 static gboolean fsexam_convert_scenario_for_name (FSEXAM_setting *setting);
       
    67 static gboolean fsexam_restore_name (FSEXAM_setting *setting, GList *list);
       
    68 static gboolean _convert_directory (FSEXAM_setting *setting, const gchar *);
       
    69 static void     convert_directory (FSEXAM_setting *setting, GNode *node);
       
    70 static gint     construct_candidate_list (GList *list, gboolean forname);
       
    71 
       
    72 
       
    73 /*
       
    74  *  Recreate symlink from file in symlink_node to file in target_node
       
    75  */
       
    76 static gboolean
       
    77 relink_symlink (GNode *symlink_node, 
       
    78                 GNode *target_node, 
       
    79                 FSEXAM_setting *setting)
       
    80 {
       
    81     gchar     *symlink_contents = NULL;
       
    82     gchar     *symlink_path = NULL;
       
    83     gchar     *target_path = NULL;
       
    84     gchar     *log_msg = NULL;
       
    85     gboolean  ret = FALSE;
       
    86 
       
    87     if ((NULL == symlink_node) || (NULL == target_node) || (NULL == setting))
       
    88         return FALSE;
       
    89 
       
    90     symlink_path = fsexam_tree_get_path (symlink_node, FALSE);
       
    91     symlink_contents = g_file_read_link (symlink_path, NULL);
       
    92     
       
    93     if (NULL == symlink_contents) {
       
    94         fsexam_errno = ERR_CANNOT_READ;
       
    95         goto done;
       
    96     }
       
    97 
       
    98     if (remove (symlink_path) == -1) {  /* delete original symlink */
       
    99         fsexam_errno = ERR_CANNOT_RM_SYMLINK;
       
   100         goto done;
       
   101     }
       
   102 
       
   103     if (*symlink_contents == '/') {     /* absolute symlink */
       
   104         target_path = fsexam_tree_get_path (target_node, FALSE);
       
   105     }else{
       
   106         /* reconstruct symlink path for relative symlink */
       
   107         gchar     *tmp = symlink_contents;
       
   108         gchar     *cur = tmp;
       
   109         GNode     *parent_node = symlink_node->parent;
       
   110         gboolean  finish = FALSE;
       
   111 
       
   112         while (!finish) {
       
   113             if ((*tmp != '/') && (*tmp != '\0')) {
       
   114                 tmp++;
       
   115                 continue;
       
   116             }else{                      /* got one subpath */
       
   117                 if (*tmp == '\0')
       
   118                     finish = TRUE;
       
   119                 else
       
   120                     *tmp = '\0';
       
   121 
       
   122                 if (strcmp (cur, ".") == 0) {
       
   123                     /* no op */
       
   124                 }else if (strcmp (cur, "..") == 0) {
       
   125                     parent_node = parent_node->parent;
       
   126                 }else if (strcmp (cur, "") == 0) {
       
   127                     /* this means '//' in the symlink_contents */
       
   128                     cur = "/";      
       
   129                 }else{
       
   130                     GNode *gnode = fsexam_tree_search_name (parent_node, cur);
       
   131                     if (gnode == NULL) {  /* fallback to use absolute path */
       
   132                         g_free (target_path);
       
   133                         target_path = fsexam_tree_get_path (target_node, FALSE);
       
   134 
       
   135                         break;
       
   136                     }else{
       
   137                         TreeNode *tnode = (TreeNode *)gnode->data;
       
   138 
       
   139                         if (TREENODE_IS_CONVERTED (tnode)) 
       
   140                             cur = TREENODE_GET_UTF8 (tnode);
       
   141                         else
       
   142                             cur = TREENODE_GET_ORIG (tnode);
       
   143                     }
       
   144                     
       
   145                     parent_node = parent_node->parent;
       
   146                 }
       
   147                
       
   148                 if (target_path == NULL) {
       
   149                     target_path = g_strdup (cur);
       
   150                 }else{
       
   151                     if (strcmp (cur, "/") == 0) {
       
   152                         cur = g_strdup_printf ("%s%s", target_path, cur);
       
   153                     }else{
       
   154                         cur = g_strdup_printf ("%s/%s", target_path, cur);
       
   155                     }
       
   156 
       
   157                     g_free (target_path);
       
   158                     target_path = cur;
       
   159                 }
       
   160 
       
   161                 cur = ++tmp;
       
   162             }
       
   163         } /* endof while */
       
   164     }
       
   165 
       
   166     if (symlink (target_path, symlink_path) == -1) {
       
   167         fsexam_errno = ERR_LOST_SYMLINK_FILE;
       
   168         goto done;
       
   169     }
       
   170 
       
   171     ret = TRUE;
       
   172 
       
   173 done:
       
   174     if (ret)    
       
   175         log_msg = _("Re-create symbolic link success"); 
       
   176 
       
   177     fsexam_log_puts (setting->log_info, symlink_path, log_msg);
       
   178     if (setting->display_msg)
       
   179         setting->display_msg (symlink_path, log_msg);
       
   180 
       
   181     g_free (symlink_contents);
       
   182     g_free (target_path);
       
   183     g_free (symlink_path);
       
   184 
       
   185     return ret;
       
   186 }
       
   187 
       
   188 /*====================================================================
       
   189  *  Function Name:  write_to_disk
       
   190  *
       
   191  *  Parameters:
       
   192  *      FSEXAM_setting *setting:
       
   193  *      gchar *path:    The dir name which contain file to be renamed
       
   194  *      gchar *orig:    The original name
       
   195  *      gchar *utf8name:    The utf8 name
       
   196  *      gchar **actualname: the actual name used if succeed
       
   197  *
       
   198  *  Desc:
       
   199  *      Rename the file from orig to utf8name. If newname exists, then will 
       
   200  *      find one new name, and actualname point to it.
       
   201  *
       
   202  *      Will handle log/history in this function, dryrun/restore
       
   203  *      may call this function directly.
       
   204  *
       
   205  *  Return value:
       
   206  *      If rename success, then return TRUE; 
       
   207  *      otherwise(such as can't rename, can't write to log) return FALSE.
       
   208  *
       
   209  *  Author:     Yandong Yao 2006/08/31
       
   210  ========================================================================*/ 
       
   211 static gboolean
       
   212 write_to_disk (FSEXAM_setting *setting, 
       
   213                const gchar *path,      /* the path of current file */
       
   214                const gchar *origname,  /* old base name            */
       
   215                const gchar *utf8name,  /* new base name            */
       
   216                short from_encoding,    /* from encoding            */
       
   217                short to_encoding,      /* to encoding              */
       
   218                gchar **actualname)      /* return the actual used   */
       
   219 {
       
   220     static gchar    oldname[PATH_MAX];
       
   221     static gchar    newname[PATH_MAX];
       
   222     gchar           *retname = NULL;
       
   223     gchar           *fname = NULL;
       
   224     gchar           *msg = NULL;
       
   225     gboolean        ret = FALSE;
       
   226 
       
   227     if ((NULL == setting) || (NULL == path) || (NULL == origname) 
       
   228             || (NULL == utf8name))
       
   229         return FALSE;
       
   230 
       
   231     fsexam_errno = ERR_OK;
       
   232 
       
   233     /* construct full old name and full new name */
       
   234     g_snprintf (oldname, PATH_MAX - 1, "%s/%s", path, origname);
       
   235     g_snprintf (newname, PATH_MAX - 1, "%s/%s", path, utf8name);
       
   236 
       
   237     /* whether newname == oldname? */
       
   238     if (strcmp (utf8name, origname) == 0) {
       
   239         fsexam_errno = ERR_NAME_SAME;
       
   240         goto done;
       
   241     }
       
   242 
       
   243     if (g_file_test (newname, G_FILE_TEST_EXISTS)) {
       
   244         fsexam_errno = ERR_NAME_EXIST;
       
   245         fname = find_non_exist_name (newname);
       
   246 
       
   247         if (fname == NULL) {
       
   248             fsexam_errno = ERR_CANNOT_RENAME;
       
   249         }else if (rename(oldname, fname) == 0){
       
   250             retname = g_strdup (basename (fname));
       
   251             ret = TRUE;
       
   252         }else{
       
   253             fsexam_errno = ERR_CANNOT_RENAME;
       
   254         }
       
   255     }else{
       
   256         if (rename(oldname, newname) == 0){
       
   257             retname = g_strdup (utf8name);
       
   258             ret = TRUE;
       
   259         }else{
       
   260             fsexam_errno = ERR_CANNOT_RENAME; /* inc dup name */
       
   261         }
       
   262     }
       
   263 
       
   264     if (ret) { 
       
   265         if (! (setting->flags & FSEXAM_SETTING_FLAGS_UNDO)) {
       
   266             gboolean same_serial = TRUE;
       
   267 
       
   268             if (setting->flags & FSEXAM_SETTING_FLAGS_DIFF_SERIAL) {
       
   269                 setting->flags &= ~FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
       
   270                 same_serial = FALSE;
       
   271             }
       
   272 
       
   273             fsexam_history_put (setting->hist_info, 
       
   274                                 ConvName, 
       
   275                                 newname,
       
   276                                 from_encoding, 
       
   277                                 to_encoding, 
       
   278                                 same_serial);
       
   279         }
       
   280 
       
   281         if (setting->update_gui) {
       
   282             setting->update_gui (setting, path, origname, retname);
       
   283         }
       
   284         
       
   285         ++setting->succ_num;
       
   286     } else {
       
   287         ++setting->fail_num;
       
   288     }
       
   289        
       
   290 done:
       
   291     msg = g_strdup_printf (_("[Name] %s -> %s"), 
       
   292                         id2encoding (from_encoding), 
       
   293                         id2encoding (to_encoding));
       
   294 
       
   295     /* Display messages to user */
       
   296     if (setting->display_msg) {
       
   297         if (ret) 
       
   298             setting->display_msg (newname, msg);
       
   299         else
       
   300             setting->display_msg (oldname, fsexam_error_get_msg ());
       
   301     }
       
   302 
       
   303     /* Writting log */
       
   304     if (fsexam_errno == ERR_OK) {
       
   305         fsexam_log_puts (setting->log_info, newname, msg);
       
   306     }else if (fsexam_errno == ERR_NAME_EXIST) {
       
   307         fsexam_log_puts (setting->log_info, fname, NULL);
       
   308     }else{
       
   309         fsexam_log_puts (setting->log_info, newname, NULL);
       
   310     }
       
   311     
       
   312     if (actualname != NULL)
       
   313         *actualname = retname;  //freed outside
       
   314     else
       
   315         g_free (retname);
       
   316 
       
   317     g_free (msg);
       
   318     g_free (fname);
       
   319 
       
   320     return ret;
       
   321 }
       
   322 
       
   323 /*====================================================================
       
   324  *  Function Name:  real_convert
       
   325  *
       
   326  *  Parameters:
       
   327  *      FSEXAM_setting *setting: 
       
   328  *      const gchar *dname: the directory in which file reside
       
   329  *      const gchar *bname: the filename without any directory name
       
   330  *      gchar **newname: return the new name when success. Need free by caller
       
   331  *
       
   332  *  Desc:
       
   333  *      real_convert() will convert the filename using provided encoding list,
       
   334  *      and display candidates to user in interactive mode.
       
   335  *
       
   336  *      If success to rename file on the disk, then newname will contain the 
       
   337  *      new name.
       
   338  *
       
   339  *      This function will handle log information also.
       
   340  *
       
   341  *      write_to_disk() will handle log either, because it can be call by
       
   342  *      other funcs.
       
   343  *
       
   344  *  Return value:
       
   345  *      If succeed to rename the file, return TRUE, otherwise return FALSE.
       
   346  *      In dryrun mode, always return FALSE.
       
   347  *
       
   348  *      The most important usage for the return value is to determine whether
       
   349  *      fsexam change the real file name on disk or not.
       
   350  *
       
   351  *  Author:     Yandong Yao 2006/09/01
       
   352  ========================================================================*/ 
       
   353 static gboolean
       
   354 real_convert (FSEXAM_setting *setting, 
       
   355               const gchar *dname, 
       
   356               const gchar *bname, 
       
   357               gchar **newname)
       
   358 {
       
   359     Score       score;
       
   360     gchar       *fullname = NULL;
       
   361     gboolean    ret = FALSE;
       
   362 
       
   363     if ((NULL == setting) || (NULL == dname) || (NULL ==bname))
       
   364         return FALSE;
       
   365 
       
   366     fsexam_errno = ERR_OK; 
       
   367 
       
   368     fullname = g_strdup_printf ("%s/%s", dname, bname);
       
   369 
       
   370     if (setting->pref->auto_detect) { /* handle encoding auto detection */
       
   371         GList *detected_encoding;
       
   372 
       
   373         detected_encoding = str_encoding_detect (bname, DEFAULT_DETECTING_FLAG);
       
   374         setting->pref->encode_list = fsexam_encoding_add_auto (
       
   375                                                 setting->pref->encode_list, 
       
   376                                                 detected_encoding);
       
   377         auto_encoding_free (detected_encoding);
       
   378     }
       
   379 
       
   380     score = fsexam_encoding_decode (setting->pref->encode_list, 
       
   381                                     ConvName, 
       
   382                                     (gchar *)bname, 
       
   383                                     strlen(bname), 
       
   384                                     setting->pref->force);
       
   385 
       
   386 
       
   387     if (setting->pref->dry_run){    /* dry run */
       
   388         ret = fsexam_dryrun_puts (setting->dryrun_info, 
       
   389                                   fullname, 
       
   390                                   score, 
       
   391                                   setting->pref->encode_list, 
       
   392                                   ConvName);    
       
   393         ret ? ++setting->succ_num : ++setting->fail_num;
       
   394     } else {                        /* real convert */  
       
   395         gint        index = 0;
       
   396         gchar       *actualname = NULL;
       
   397         Encoding    *encoding = NULL;
       
   398 
       
   399         if ((score == FAIL) || (score == ORIGINAL)){
       
   400             fsexam_errno = (score == FAIL) ? ERR_NO_PROPER_ENCODING 
       
   401                                            : ERR_NAME_UTF8_ALREADY;
       
   402             fsexam_log_puts_folder_and_name (setting->log_info, 
       
   403                                              dname, bname, 
       
   404                                              NULL);
       
   405             if (setting->display_msg)
       
   406                 setting->display_msg (fullname, fsexam_error_get_msg());
       
   407 
       
   408             goto done;
       
   409         }   
       
   410        
       
   411 	    /* 
       
   412 	     * User may select don't ask me again
       
   413 	     */ 
       
   414 	    if (setting->gold_index != -1) {
       
   415             index = setting->gold_index;
       
   416 	    } else if (setting->pref->auto_conversion) {
       
   417             index = fsexam_encoding_get_first_index (
       
   418                                         setting->pref->encode_list);
       
   419         } else {
       
   420             index = setting->get_index (setting->pref->encode_list, TRUE);
       
   421         }
       
   422 
       
   423         if (index == -1) {          /* cancel the selection */
       
   424             fsexam_errno = ERR_CANCEL_CONVERSION;
       
   425             goto done;
       
   426         }
       
   427 
       
   428         encoding = (Encoding *)g_list_nth_data (setting->pref->encode_list, 
       
   429                                                 index);
       
   430         if (NULL == encoding){
       
   431             fsexam_errno = ERR_ENCODING_INDEX_INVALID;
       
   432             fsexam_log_puts_folder_and_name (setting->log_info, 
       
   433                                              dname, bname, 
       
   434                                              NULL);
       
   435             if (setting->display_msg)
       
   436                 setting->display_msg (fullname, fsexam_error_get_msg());
       
   437 
       
   438             goto done;
       
   439         }
       
   440 
       
   441         ret = write_to_disk (setting, 
       
   442                              dname, 
       
   443                              bname, 
       
   444                              encoding->u.converted_text, 
       
   445                              encoding->encodingID, 
       
   446                              encoding2id ("UTF-8"),
       
   447                              &actualname);
       
   448 
       
   449         if ((ret) && (newname)) {   // Return new name
       
   450             *newname = actualname;
       
   451         }else{
       
   452             g_free (actualname);
       
   453         }
       
   454     }
       
   455     
       
   456 done:
       
   457     if (setting->pref->auto_detect)
       
   458         setting->pref->encode_list = fsexam_encoding_remove_auto (
       
   459                                                 setting->pref->encode_list);
       
   460 
       
   461     g_free (fullname);
       
   462 
       
   463     return ret;
       
   464 }
       
   465 
       
   466 /*
       
   467  * convert one directory recursively. won't care about symlink
       
   468  */
       
   469 static gboolean
       
   470 common_convert_directory (FSEXAM_setting *setting, 
       
   471                           const gchar *dname, 
       
   472                           gboolean restore)
       
   473 {
       
   474     struct stat     statbuf;
       
   475     struct dirent   *dirp = NULL;
       
   476     DIR             *dp = NULL;
       
   477     gchar           *childname = NULL;
       
   478     gboolean        ret = FALSE;
       
   479 
       
   480     if ((NULL == dname) || (NULL == setting))
       
   481         return FALSE;
       
   482 
       
   483     if ((dp = opendir (dname)) == NULL){
       
   484         fsexam_errno = ERR_CANNOT_OPEN;
       
   485         fsexam_log_puts (setting->log_info, dname, NULL);
       
   486             if (setting->display_msg)
       
   487                 setting->display_msg (dname, fsexam_error_get_msg());
       
   488 
       
   489         return ret;
       
   490     }
       
   491 
       
   492     while ((dirp = readdir (dp)) != NULL){
       
   493         if ((strcmp (dirp->d_name, ".") == 0)
       
   494                 || (strcmp (dirp->d_name, "..") == 0))
       
   495             continue;
       
   496 
       
   497         fsexam_errno = ERR_OK;
       
   498 
       
   499         childname = g_strdup_printf ("%s/%s", dname, dirp->d_name);
       
   500 
       
   501         /* need convert this filename or not? */
       
   502         if (lstat (childname, &statbuf) == -1) 
       
   503             fsexam_errno = ERR_FILE_NONEXIST;
       
   504         else if (!(S_ISREG(statbuf.st_mode)) 
       
   505                 && !(S_ISDIR(statbuf.st_mode)) && !(S_ISLNK(statbuf.st_mode))){
       
   506             fsexam_errno = ERR_FILE_TYPE_NOT_SUPPORT; 
       
   507         }else if ((! setting->pref->hidden) && (*(dirp->d_name) == '.')){
       
   508             fsexam_errno = ERR_IGNORE_HIDDEN_FILE;
       
   509         }else if ((!setting->pref->remote) 
       
   510                 && (is_remote_file (setting->remote_path, childname))) {
       
   511             fsexam_errno = ERR_IGNORE_REMOTE_FILE;
       
   512         } 
       
   513 
       
   514         if (fsexam_errno != ERR_OK) {
       
   515             fsexam_log_puts (setting->log_info, childname, NULL);
       
   516             if (setting->display_msg)
       
   517                 setting->display_msg (childname, fsexam_error_get_msg());
       
   518             g_free (childname);
       
   519             continue;
       
   520         }
       
   521     
       
   522         if ((!setting->pref->force) 
       
   523                 && (str_isutf8 (dirp->d_name, DEFAULT_DETECTING_FLAG))) {
       
   524             fsexam_errno = ERR_NAME_UTF8_ALREADY;
       
   525         }
       
   526 
       
   527         if (restore) {
       
   528             //Currently tree for restore name has no -R node, so no-op
       
   529         }else if (fsexam_errno != ERR_NAME_UTF8_ALREADY) {
       
   530             gchar  *newname = NULL;
       
   531 
       
   532             ret = real_convert (setting, dname, dirp->d_name, &newname);
       
   533 
       
   534             if (ret && newname != NULL){
       
   535                 g_free (childname);
       
   536                 childname = g_strdup_printf ("%s/%s", dname, newname);
       
   537                 lstat(childname, &statbuf); 
       
   538             }
       
   539                 
       
   540             g_free (newname);
       
   541         }
       
   542 
       
   543         if (setting->flags & FSEXAM_SETTING_FLAGS_STOP) {
       
   544             g_free (childname);
       
   545             goto done;
       
   546         }
       
   547 
       
   548         if (! setting->pref->dry_run 
       
   549                 && setting->pref->special 
       
   550                 && fsexam_special_is_special_for_name (childname, setting)) {
       
   551             gchar *hist_search_path;
       
   552 
       
   553             /* history search path is for search Hist_item in history file */
       
   554             hist_search_path = g_strdup_printf ("%s/%s", dname, dirp->d_name);
       
   555             fsexam_compress_convert_name (setting, 
       
   556                                           childname, 
       
   557                                           hist_search_path, 
       
   558                                           restore);
       
   559             g_free (hist_search_path);
       
   560         } else if (S_ISDIR(statbuf.st_mode)) {
       
   561             _convert_directory(setting, childname);
       
   562         }
       
   563 
       
   564         g_free (childname);
       
   565     }
       
   566 
       
   567 done:
       
   568     closedir (dp);
       
   569 
       
   570     return ret;
       
   571 }
       
   572 
       
   573 static gboolean
       
   574 _convert_directory (FSEXAM_setting *setting, const gchar *dname)
       
   575 {
       
   576     return common_convert_directory (setting, dname, FALSE);
       
   577 }
       
   578 
       
   579 /*
       
   580 static gboolean
       
   581 _restore_directory (FSEXAM_setting *setting, const gchar *dname)
       
   582 {
       
   583     return common_convert_directory (setting, dname, TRUE);
       
   584 }
       
   585 */
       
   586 
       
   587 /*
       
   588  *  convert to fullpath, then call _convert_directory
       
   589  */
       
   590 static void
       
   591 convert_directory (FSEXAM_setting *setting, GNode *node)
       
   592 {
       
   593     gchar *dir = fsexam_tree_get_path (node, FALSE); 
       
   594 
       
   595     _convert_directory (setting, dir);
       
   596 
       
   597     return;
       
   598 }
       
   599 
       
   600 /*
       
   601  * Convert the symlink target, and convert parent first if has not do.
       
   602  */
       
   603 static gboolean
       
   604 convert_symlink_target_node (GNode *node, 
       
   605                              FSEXAM_setting *setting, 
       
   606                              gboolean restore)
       
   607 {
       
   608     TreeNode    *tnode = NULL;
       
   609 
       
   610     if ((NULL == node) || (NULL == setting))
       
   611         return FALSE;
       
   612     
       
   613     tnode = (TreeNode *)node->data;
       
   614 
       
   615     if (TREENODE_IS_TRAVERSED (tnode)) {
       
   616         return TREENODE_IS_CONVERTED (tnode) ? TRUE : FALSE;
       
   617     }
       
   618 
       
   619     if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
       
   620         return FALSE;    /* stop the recursive */
       
   621 
       
   622     if ((node->parent != NULL) 
       
   623         && (! TREENODE_IS_TRAVERSED ((TreeNode *)((node->parent)->data)))) {
       
   624         convert_symlink_target_node (node->parent, setting, restore);
       
   625     }
       
   626 
       
   627     if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
       
   628         return FALSE;    /* stop the recursive */
       
   629 
       
   630     if (restore)
       
   631         return wrapped_restore_convert_node (node, (gpointer)setting);
       
   632     else
       
   633         return wrapped_convert_node (node, (gpointer)setting);
       
   634 }
       
   635 
       
   636 /*============================================================================
       
   637  * Function Name:   convert_node
       
   638  *
       
   639  * Parameters:
       
   640  *      FSEXAM_setting *:   contain preference information
       
   641  *      GNode *node:        The node which will be handled
       
   642  *
       
   643  * Desc:
       
   644  *      GNode Traverse function. Convert one node in the whole tree. 
       
   645  *      Need care about recursive flag
       
   646  *
       
   647  *      Because this function will called by g_node_traverse, so we need 
       
   648  *      handle various error information here.
       
   649  *
       
   650  * Return value:
       
   651  *      Return TRUE to stop g_node_traverse().
       
   652  *
       
   653  * Author:
       
   654  *      Yandong Yao 2006/08/22
       
   655  ============================================================================*/
       
   656 static gboolean 
       
   657 convert_node (GNode *node, gpointer data)
       
   658 {
       
   659     FSEXAM_setting  *setting = (FSEXAM_setting *)data;
       
   660 
       
   661     wrapped_convert_node (node, setting);
       
   662 
       
   663     if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
       
   664         return TRUE;
       
   665 
       
   666     return FALSE;
       
   667 }
       
   668 
       
   669 /*
       
   670  *  Return TRUE if current node has been converted.
       
   671  */
       
   672 static gboolean
       
   673 wrapped_convert_node (GNode *node, FSEXAM_setting *setting)
       
   674 {
       
   675     TreeNode        *tnode = node->data;
       
   676     gboolean        node_converted = FALSE;
       
   677     gchar           *fullpath = NULL;
       
   678 
       
   679     if ((NULL == setting) || (NULL == tnode) 
       
   680             || (TREENODE_IS_TRAVERSED (tnode))){
       
   681         return FALSE;
       
   682     }
       
   683 
       
   684     TREENODE_SET_TRAVERSED (tnode, 1);  /* avoid infinite loop for symlink */
       
   685 
       
   686     fsexam_errno = ERR_OK;
       
   687 
       
   688     /* get stored error during tree construction */
       
   689     if (TREENODE_FLAG_REMOTE_FILE (tnode)) {
       
   690         fsexam_errno = ERR_IGNORE_REMOTE_FILE;
       
   691     }else if (TREENODE_FLAG_HIDDEN_FILE (tnode)) {
       
   692         fsexam_errno = ERR_IGNORE_HIDDEN_FILE;
       
   693     }else if (TREENODE_FLAG_UTF8_ALREADY (tnode)) {
       
   694         fsexam_errno = ERR_NAME_UTF8_ALREADY;
       
   695     }
       
   696 
       
   697     if (fsexam_errno != ERR_OK) {
       
   698         fullpath = fsexam_tree_get_path (node, FALSE);
       
   699         fsexam_log_puts (setting->log_info, fullpath, NULL);
       
   700         if (setting->display_msg) {
       
   701             setting->display_msg (fullpath, fsexam_error_get_msg ());
       
   702         }
       
   703 
       
   704         if (fsexam_errno != ERR_NAME_UTF8_ALREADY)
       
   705             goto done;
       
   706     }
       
   707 
       
   708     if (TREENODE_IS_NEED_CONVERT (tnode)) {
       
   709         gchar    *dir = fsexam_tree_get_path (node->parent, FALSE);
       
   710         gchar    *bname = tnode->orig;
       
   711         gchar    *newname = NULL;
       
   712 
       
   713         node_converted = real_convert (setting, dir, bname, &newname);
       
   714 
       
   715         if (node_converted && newname != NULL) {
       
   716             TREENODE_SET_CONVERTED (tnode, 1);
       
   717             TREENODE_SET_UTF8 (tnode, newname);
       
   718         }
       
   719         g_free (fullpath);
       
   720         fullpath = g_strdup_printf ("%s/%s", dir, newname ? newname : bname);
       
   721 
       
   722         g_free (dir);
       
   723 
       
   724         if (setting->flags & FSEXAM_SETTING_FLAGS_STOP)
       
   725             goto done;
       
   726     }
       
   727 
       
   728     if (TREENODE_IS_SPECIAL (tnode)) {
       
   729         if (fullpath == NULL)
       
   730                 fullpath = fsexam_tree_get_path (node, FALSE);
       
   731 
       
   732         /* 
       
   733          * archive or compress filename may be converted already,
       
   734          * so get the original name.
       
   735          */
       
   736         gchar *hist_search_path = fsexam_tree_get_path (node, TRUE);
       
   737 
       
   738         fsexam_compress_convert_name (setting, 
       
   739                                       fullpath, 
       
   740                                       hist_search_path, 
       
   741                                       FALSE);
       
   742 
       
   743         g_free (hist_search_path);
       
   744     }else if (TREENODE_IS_SYMLINK (tnode)) {
       
   745         if (TREENODE_FLAG_TARGET_NOTEXIST (tnode)) {
       
   746             if (fullpath == NULL)
       
   747                 fullpath = fsexam_tree_get_path (node, FALSE);
       
   748             
       
   749             fsexam_errno = ERR_SYMLINK_TARGET_NOEXIST;
       
   750             fsexam_log_puts (setting->log_info, fullpath, NULL);
       
   751             if (setting->display_msg) {
       
   752                 setting->display_msg (fullpath, fsexam_error_get_msg ());
       
   753             }
       
   754         }else if (!setting->pref->no_check_symlink_content) {
       
   755             /* follow has been handled during tree construction */
       
   756             GNode   *gnode = TREENODE_GET_TARGET (tnode);
       
   757             
       
   758             if ((gnode != NULL) && (gnode->data != NULL)) 
       
   759                 if (convert_symlink_target_node (gnode, setting, FALSE)) 
       
   760                     relink_symlink (node, gnode, setting);
       
   761         }
       
   762     } else if ((setting->pref->recursive && TREENODE_IS_RECURSIVE (tnode))
       
   763             && (!setting->pref->follow) 
       
   764             && setting->pref->no_check_symlink_content){
       
   765         convert_directory (setting, node);
       
   766     }
       
   767 
       
   768 done:
       
   769     g_free (fullpath);
       
   770 
       
   771     return node_converted;
       
   772 }
       
   773 
       
   774 /*====================================================================
       
   775  *  Function Name:  dryrun_convert_node
       
   776  *
       
   777  *  Parameters:
       
   778  *      GNode *node:    one node created from dryrun result file.
       
   779  *      gpointer data:  is FSEXAM_setting passed from g_node_traverse.
       
   780  *
       
   781  *  Desc:
       
   782  *      Convert one node come from dryrun result file.
       
   783  *
       
   784  *      We need handle various error here.
       
   785  *
       
   786  *  Return value:
       
   787  *      Always return TRUE, otherwise g_node_traverse will stop.
       
   788  *
       
   789  *  Exception:
       
   790  *
       
   791  *  Author:     Yandong Yao 2006/09/05
       
   792  ========================================================================*/ 
       
   793 static gboolean
       
   794 dryrun_convert_node (GNode *node, gpointer data)
       
   795 {
       
   796     FSEXAM_setting  *setting = (FSEXAM_setting *)data;
       
   797     TreeNode        *tnode = node->data;
       
   798     gchar            *path = NULL;
       
   799     gboolean        ret;
       
   800 
       
   801     if ((NULL == setting)
       
   802             || (NULL == tnode)
       
   803             || TREENODE_IS_TRAVERSED (tnode) 
       
   804             || (TREENODE_GET_UTF8 (tnode) == NULL) 
       
   805             || (! TREENODE_IS_NEED_CONVERT (tnode))) {   /* Ingore symlink */
       
   806         return FALSE;
       
   807     }
       
   808 
       
   809     TREENODE_SET_TRAVERSED (tnode, 1);
       
   810 
       
   811     path = fsexam_tree_get_path (node->parent, FALSE);  /* get real path */
       
   812 
       
   813     ret = write_to_disk (setting, 
       
   814                         path, 
       
   815                         TREENODE_GET_ORIG (tnode),
       
   816                         TREENODE_GET_UTF8 (tnode),
       
   817                         TREENODE_GET_ID (tnode),        /* from encoding    */
       
   818                         encoding2id ("UTF-8"),          /* to encoding      */
       
   819                         NULL);
       
   820 
       
   821     /* update TreeNode */
       
   822     if (ret) {
       
   823         TREENODE_SET_CONVERTED (tnode, 1);  
       
   824     }
       
   825 
       
   826     g_free (path);
       
   827 
       
   828     return FALSE;
       
   829 }
       
   830 
       
   831 /*====================================================================
       
   832  *  Function Name:  restore_convert_node
       
   833  *
       
   834  *  Parameters:
       
   835  *      GNode *node:   node need restore
       
   836  *      gpointer data: is FSEXAM_setting passed from g_node_traverse.
       
   837  *
       
   838  *  Desc:
       
   839  *      restore one node come from history file
       
   840  *      We need handle various error here.
       
   841  *
       
   842  *  Return value:
       
   843  *      Always return FALSE, otherwise g_node_traverse will stop.
       
   844  *
       
   845  *  Author:     Yandong Yao 2006/11/27
       
   846  ========================================================================*/ 
       
   847 static gboolean
       
   848 restore_convert_node (GNode *node, gpointer data)
       
   849 {
       
   850     wrapped_restore_convert_node (node, data);
       
   851 
       
   852     return FALSE;
       
   853 }
       
   854 
       
   855 /* 
       
   856  * Return TRUE if node has been converted.
       
   857  */
       
   858 static gboolean
       
   859 wrapped_restore_convert_node (GNode *node, gpointer data)
       
   860 {
       
   861     FSEXAM_setting  *setting = (FSEXAM_setting *)data;  
       
   862     TreeNode        *tnode = (node->data);
       
   863     gboolean        node_converted = FALSE;
       
   864     gchar           *dirpath = NULL;
       
   865     gchar           *fullpath = NULL;
       
   866 
       
   867     if ((NULL == setting) || (NULL == tnode) 
       
   868             || TREENODE_IS_TRAVERSED (tnode)) { 
       
   869         return FALSE;
       
   870     }
       
   871 
       
   872     TREENODE_SET_TRAVERSED (tnode, 1);
       
   873 
       
   874     dirpath = fsexam_tree_get_path (node->parent, FALSE);
       
   875     fullpath = fsexam_tree_get_path (node, FALSE);
       
   876 
       
   877     if (dirpath == NULL) {
       
   878         g_free (fullpath);
       
   879         return FALSE;
       
   880     }
       
   881 
       
   882     fsexam_errno = ERR_OK;
       
   883 
       
   884     /* do we need convert it? */
       
   885     if (TREENODE_FLAG_REMOTE_FILE (tnode)) {
       
   886         fsexam_errno = ERR_IGNORE_REMOTE_FILE;
       
   887     }else if (TREENODE_FLAG_HIDDEN_FILE (tnode)) {
       
   888         fsexam_errno = ERR_IGNORE_HIDDEN_FILE;
       
   889     }
       
   890 
       
   891     if (fsexam_errno != ERR_OK) {
       
   892         fsexam_log_puts (setting->log_info, fullpath, NULL);
       
   893         if (setting->display_msg) {
       
   894             setting->display_msg (fullpath, fsexam_error_get_msg ());
       
   895         }
       
   896 
       
   897         goto done;
       
   898     }
       
   899 
       
   900     if (TREENODE_GET_ORIG (tnode) != NULL && TREENODE_GET_UTF8 (tnode) != NULL){
       
   901         if (TREENODE_IS_REVERSE (tnode)) {
       
   902             node_converted = write_to_disk (setting,
       
   903                                             dirpath,
       
   904                                             TREENODE_GET_ORIG (tnode),
       
   905                                             TREENODE_GET_UTF8 (tnode),
       
   906                                             encoding2id ("UTF-8"),
       
   907                                             TREENODE_GET_ID (tnode),
       
   908                                             NULL);
       
   909 
       
   910         }else{
       
   911             node_converted = write_to_disk (setting,
       
   912                                             dirpath,
       
   913                                             TREENODE_GET_ORIG (tnode),
       
   914                                             TREENODE_GET_UTF8 (tnode),
       
   915                                             TREENODE_GET_ID (tnode),
       
   916                                             encoding2id ("UTF-8"),
       
   917                                             NULL);
       
   918         }
       
   919 
       
   920         if (node_converted)
       
   921             TREENODE_SET_CONVERTED (tnode, 1);
       
   922 
       
   923         g_free (fullpath);
       
   924         fullpath = fsexam_tree_get_path (node, FALSE);
       
   925     }
       
   926 
       
   927     if (TREENODE_IS_SPECIAL (tnode)) {
       
   928         /* hist_search_path use the original name of special file */
       
   929         gchar *hist_search_path = fsexam_tree_get_path (node, TRUE);
       
   930 
       
   931         /* Restore special type file name */
       
   932         fsexam_compress_convert_name (setting, 
       
   933                                       fullpath, 
       
   934                                       hist_search_path, 
       
   935                                       TRUE);
       
   936 
       
   937         g_free (hist_search_path);
       
   938     }else if (TREENODE_IS_SYMLINK (tnode)) {
       
   939         if (TREENODE_FLAG_TARGET_NOTEXIST (tnode)) {
       
   940             fsexam_errno = ERR_SYMLINK_TARGET_NOEXIST;
       
   941             fsexam_log_puts (setting->log_info, fullpath, NULL);
       
   942             if (setting->display_msg) {
       
   943                 setting->display_msg (fullpath, fsexam_error_get_msg ());
       
   944             }
       
   945         }else if (!setting->pref->no_check_symlink_content) {
       
   946             GNode   *gnode = TREENODE_GET_TARGET (tnode);
       
   947 
       
   948             if ((gnode != NULL) && (gnode->data != NULL)) 
       
   949                 if (convert_symlink_target_node (gnode, setting, TRUE)) 
       
   950                     relink_symlink (node, gnode, setting);
       
   951         }
       
   952     }
       
   953 
       
   954 done:
       
   955     g_free (fullpath);
       
   956     g_free (dirpath);
       
   957 
       
   958     return node_converted;
       
   959 }
       
   960 
       
   961 /*
       
   962  *  Display candidate list for CLI.
       
   963  *  Return the total number of candidates.
       
   964  */
       
   965 static gint
       
   966 construct_candidate_list (GList *list, gboolean forname)
       
   967 {
       
   968     gint num_candidate = 0;
       
   969 
       
   970     if (list == NULL)
       
   971         return -1;
       
   972 
       
   973     fprintf (stdout, _("Candidate list:\n"));
       
   974     fprintf (stdout, _("\t[No.] Encoding name\tConversion Result\n"));
       
   975 
       
   976     while (list != NULL) {
       
   977         Encoding *encode = (Encoding *)list->data;
       
   978 
       
   979         list = g_list_next (list);
       
   980 
       
   981         if (encode->score == FAIL) 
       
   982             continue;
       
   983 
       
   984         if (forname)
       
   985             fprintf (stdout, "\t[%i]   %s\t\t%s\n", 
       
   986                      num_candidate, 
       
   987                      id2encoding (encode->encodingID),
       
   988                      encode->u.converted_text);
       
   989         else {                          /* content */
       
   990             fprintf (stdout, "\t[%i]   %s\t\t%s\n", 
       
   991                      num_candidate, 
       
   992                      id2encoding (encode->encodingID),
       
   993                      encode->u.contents);
       
   994         }
       
   995 
       
   996         num_candidate++;
       
   997     }
       
   998 
       
   999     return num_candidate;
       
  1000 }
       
  1001 
       
  1002 static gboolean
       
  1003 fsexam_convert_scenario_for_name (FSEXAM_setting *setting)
       
  1004 {
       
  1005     g_return_val_if_fail (setting != NULL, FALSE);
       
  1006 
       
  1007     GSList   *slist = NULL;
       
  1008     GNode    *root = NULL;
       
  1009     gboolean ret;
       
  1010 
       
  1011     ret = fsexam_dryrun_process (setting->dryrun_info, &slist);
       
  1012 
       
  1013     if ((! ret) || (NULL == slist)) {
       
  1014         fsexam_log_puts (setting->log_info, NULL, NULL);
       
  1015         if (setting->display_msg)
       
  1016             setting->display_msg (NULL, fsexam_error_get_msg ());
       
  1017 
       
  1018         setting->display_stats (setting);   /* display statastics */
       
  1019         fsexam_log_flush (setting->log_info);
       
  1020         return FALSE;
       
  1021     }
       
  1022 
       
  1023     root = fsexam_tree_construct_from_dryrun (slist, setting);
       
  1024     fsexam_dryrun_item_slist_free (slist);
       
  1025 
       
  1026     if (root == NULL) {
       
  1027         fsexam_errno = ERR_TREE_IS_EMPTY;
       
  1028         fsexam_log_puts (setting->log_info, NULL, NULL);
       
  1029         if (setting->display_msg)
       
  1030             setting->display_msg (NULL, fsexam_error_get_msg ());
       
  1031 
       
  1032         return FALSE;
       
  1033     }
       
  1034 
       
  1035     if (fsexam_debug () 
       
  1036             & (FSEXAM_DBG_OPTION | FSEXAM_DBG_TREE | FSEXAM_DBG_FILELIST)) {
       
  1037         return TRUE;
       
  1038     }
       
  1039 
       
  1040     setting->flags |= FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
       
  1041 
       
  1042     g_node_traverse (root, 
       
  1043                     G_PRE_ORDER, 
       
  1044                     G_TRAVERSE_ALL, 
       
  1045                     -1, 
       
  1046                     dryrun_convert_node, 
       
  1047                     setting);
       
  1048 
       
  1049     setting->display_stats (setting);   /* display statastics */
       
  1050     fsexam_log_flush (setting->log_info);
       
  1051     setting->flags = 0;
       
  1052     fsexam_tree_destroy (root);
       
  1053 
       
  1054     return TRUE;
       
  1055 
       
  1056 }
       
  1057 
       
  1058 static gboolean
       
  1059 fsexam_restore_name (FSEXAM_setting *setting, GList *list)
       
  1060 {
       
  1061     g_return_val_if_fail (setting != NULL, FALSE);
       
  1062 
       
  1063     GNode *root = NULL;
       
  1064 
       
  1065     root = fsexam_tree_construct_from_history (list, 
       
  1066                                                setting->hist_info, 
       
  1067                                                setting);
       
  1068 
       
  1069     if ((list == NULL) || (root == NULL)){
       
  1070         fsexam_errno = ERR_TREE_IS_EMPTY;
       
  1071         fsexam_log_puts (setting->log_info, NULL, NULL);
       
  1072         if (setting->display_msg)
       
  1073             setting->display_msg (NULL, fsexam_error_get_msg ());
       
  1074 
       
  1075         return FALSE;
       
  1076     }
       
  1077 
       
  1078     setting->flags |= FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
       
  1079 
       
  1080     if (fsexam_debug () 
       
  1081             & (FSEXAM_DBG_OPTION | FSEXAM_DBG_TREE | FSEXAM_DBG_FILELIST)) {
       
  1082         return TRUE;
       
  1083     }
       
  1084 
       
  1085     g_node_traverse (root, 
       
  1086                     G_PRE_ORDER, 
       
  1087                     G_TRAVERSE_ALL, 
       
  1088                     -1, 
       
  1089                     restore_convert_node, 
       
  1090                     setting);
       
  1091 
       
  1092     setting->display_stats (setting);   /* display statastics */
       
  1093     fsexam_log_flush (setting->log_info);
       
  1094     setting->flags = 0;
       
  1095     fsexam_tree_destroy (root);
       
  1096 
       
  1097     return TRUE;
       
  1098 }
       
  1099 
       
  1100 /* --------------   Public API  ------------------------ */
       
  1101 
       
  1102 /*
       
  1103  *  find one non exist name through append suffix.
       
  1104  *  need free by caller
       
  1105  */
       
  1106 gchar *
       
  1107 find_non_exist_name (const gchar *name)
       
  1108 {
       
  1109     gchar   tmp[PATH_MAX];
       
  1110     gint    i;
       
  1111 
       
  1112     for (i = 0; ; i++) {
       
  1113         if (i) {
       
  1114             g_snprintf (tmp, PATH_MAX - 1, "%s.%s.%d", name, SUFFIX, i);
       
  1115         }else{
       
  1116             g_snprintf (tmp, PATH_MAX - 1, "%s.%s", name, SUFFIX);
       
  1117         }
       
  1118 
       
  1119         if (! g_file_test (tmp, G_FILE_TEST_EXISTS))
       
  1120             return g_strdup (tmp);
       
  1121     }
       
  1122 }
       
  1123 
       
  1124 /*
       
  1125  * Convert single file's fullpath, don't care any flags
       
  1126  */
       
  1127 gboolean
       
  1128 fsexam_convert_single_filename (FSEXAM_setting *setting, 
       
  1129                                 const gchar *filename,
       
  1130                                 gchar **result_name)
       
  1131 {
       
  1132     GNode    *root = NULL;
       
  1133     GNode    *tmp = NULL;
       
  1134     gboolean node_converted = FALSE;
       
  1135 
       
  1136     if ((filename == NULL) || (setting == NULL)) {
       
  1137         return FALSE;
       
  1138     }
       
  1139 
       
  1140     fsexam_setting_reset_stats (setting);
       
  1141 
       
  1142     if ((root = fsexam_tree_construct_from_single_file (filename)) == NULL) {
       
  1143         fsexam_errno = ERR_TREE_IS_EMPTY;
       
  1144         fsexam_log_puts (setting->log_info, NULL, NULL);
       
  1145         if (setting->display_msg)
       
  1146             setting->display_msg (NULL, fsexam_error_get_msg ());
       
  1147 
       
  1148         return FALSE;
       
  1149     }
       
  1150 
       
  1151     setting->flags |= FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
       
  1152 
       
  1153     tmp = root;
       
  1154     while ((tmp = g_node_first_child (tmp)) != NULL) {
       
  1155         TreeNode *tnode = tmp->data;
       
  1156         gchar    *bname = tnode->orig;
       
  1157         gchar    *dir = NULL;
       
  1158         gchar    *newname = NULL;
       
  1159 
       
  1160         if (! TREENODE_IS_NEED_CONVERT (tnode)) {
       
  1161             continue;
       
  1162         }
       
  1163 
       
  1164         dir = fsexam_tree_get_path (tmp->parent, FALSE);
       
  1165         node_converted = real_convert (setting, dir, bname, &newname);
       
  1166         g_free (dir);
       
  1167 
       
  1168         if (node_converted) {
       
  1169             TREENODE_SET_CONVERTED (tnode, 1);
       
  1170             TREENODE_SET_UTF8 (tnode, newname);
       
  1171 
       
  1172             if ((G_NODE_IS_LEAF (tmp)) && (result_name != NULL)) {
       
  1173                 /* last node */ 
       
  1174                 *result_name = fsexam_tree_get_path (tmp, FALSE);
       
  1175             }
       
  1176         }else{
       
  1177             g_free (newname);
       
  1178             break;      /* fsexam_errno store the reason */
       
  1179         }
       
  1180     }
       
  1181     
       
  1182     setting->flags = 0;
       
  1183     fsexam_tree_destroy (root);
       
  1184 
       
  1185     return node_converted;
       
  1186 }
       
  1187 
       
  1188 gboolean
       
  1189 fsexam_convert_filename (FSEXAM_setting *setting, const gchar *filename)
       
  1190 {
       
  1191     g_return_val_if_fail (setting != NULL, FALSE);
       
  1192 
       
  1193     GList       *list = NULL;
       
  1194     gboolean    ret;
       
  1195 
       
  1196     if (filename != NULL) 
       
  1197         list = g_list_prepend (list, (gpointer) filename);
       
  1198 
       
  1199     ret = fsexam_convert_filename_batch (setting, list);
       
  1200 
       
  1201     fsexam_list_free (list);
       
  1202 
       
  1203     return ret;
       
  1204 }
       
  1205 
       
  1206 gboolean
       
  1207 fsexam_convert_filename_batch (FSEXAM_setting *setting, GList *list)
       
  1208 {
       
  1209     g_return_val_if_fail (setting != NULL, FALSE);
       
  1210 
       
  1211     GNode *root = NULL;
       
  1212 
       
  1213     fsexam_setting_reset_stats (setting);
       
  1214 
       
  1215     if (list != NULL) 
       
  1216         root = fsexam_tree_construct_from_list (list, setting);
       
  1217 
       
  1218     if ((NULL == list) || (NULL == root)){
       
  1219         fsexam_errno = ERR_TREE_IS_EMPTY;
       
  1220         fsexam_log_puts (setting->log_info, NULL, NULL);
       
  1221         if (setting->display_msg)
       
  1222             setting->display_msg (NULL, fsexam_error_get_msg ());
       
  1223 
       
  1224         return FALSE;
       
  1225     }
       
  1226 
       
  1227     if (fsexam_debug () 
       
  1228             & (FSEXAM_DBG_OPTION | FSEXAM_DBG_TREE | FSEXAM_DBG_FILELIST)) {
       
  1229         return TRUE;
       
  1230     }
       
  1231 
       
  1232     setting->flags |= FSEXAM_SETTING_FLAGS_DIFF_SERIAL;
       
  1233 
       
  1234     g_node_traverse (root, 
       
  1235                      G_PRE_ORDER, 
       
  1236                      G_TRAVERSE_ALL, 
       
  1237                      -1, 
       
  1238                      convert_node,  /* Return TRUE will stop the traverse */
       
  1239                      setting);
       
  1240 
       
  1241     setting->display_stats (setting);   /* display statastics */
       
  1242     fsexam_log_flush (setting->log_info);
       
  1243     setting->flags = 0;             /* clear all flags after conversion */
       
  1244     fsexam_tree_destroy (root);
       
  1245 
       
  1246     return TRUE;
       
  1247 }
       
  1248 
       
  1249 
       
  1250 //TODO: Add format validation
       
  1251 gboolean
       
  1252 fsexam_convert_scenario (FSEXAM_setting *setting)
       
  1253 {
       
  1254     g_return_val_if_fail (setting != NULL, FALSE);
       
  1255 
       
  1256     ConvType    type;
       
  1257     gboolean    ret;
       
  1258 
       
  1259     fsexam_setting_reset_stats (setting);
       
  1260 
       
  1261     if (setting->dryrun_info == NULL) {
       
  1262         fprintf (stderr, _("Can't access dry run result information.\n"));
       
  1263         return FALSE;
       
  1264     }
       
  1265 
       
  1266     if (!fsexam_dryrun_get_convtype (setting->dryrun_info, &type))
       
  1267         return FALSE;
       
  1268 
       
  1269     if (type == ConvName) {
       
  1270         ret = fsexam_convert_scenario_for_name (setting);
       
  1271     }else{
       
  1272         ret = fsexam_convert_scenario_for_content (setting);
       
  1273     }
       
  1274 
       
  1275     return ret;
       
  1276 }
       
  1277 
       
  1278 gboolean 
       
  1279 fsexam_restore (FSEXAM_setting *setting, GList *list, ConvType restore_type)
       
  1280 {
       
  1281     gboolean ret;
       
  1282 
       
  1283     fsexam_setting_reset_stats (setting);
       
  1284 
       
  1285     if (restore_type == RestoreConvName) {
       
  1286         ret = fsexam_restore_name (setting, list);
       
  1287     }else if (restore_type == RestoreConvContent) {
       
  1288         ret = fsexam_restore_content (setting, list);
       
  1289     }else{
       
  1290         g_print (_("Don't support this restore type.\n"));
       
  1291         ret = FALSE;
       
  1292     }
       
  1293 
       
  1294     return ret;
       
  1295 }
       
  1296 
       
  1297 
       
  1298 /* ---------------  call back function for CLI ------------  */
       
  1299 
       
  1300 #define INPUT_LEN 10
       
  1301 
       
  1302 /*
       
  1303  *  Get the index of user's selection.
       
  1304  *  -1 returned if no selection.
       
  1305  */
       
  1306 gint 
       
  1307 get_index_default (GList *encoding_list, gboolean forname)
       
  1308 {
       
  1309     gint     num_candidate;
       
  1310     gchar    input[INPUT_LEN];
       
  1311     gint     response;
       
  1312     gint     result_index;
       
  1313 
       
  1314     num_candidate = construct_candidate_list (encoding_list, forname);
       
  1315 
       
  1316     if (num_candidate < 1) {
       
  1317         fprintf (stderr, _("No proper encoding.\n"));
       
  1318         return -1;
       
  1319     }
       
  1320 
       
  1321     do {
       
  1322         fprintf (stdout, _("Please select No.('s' to skip): "));
       
  1323         memset (input, 0, sizeof (input));  
       
  1324         fflush (stdin);             /* discard excess data */
       
  1325         fgets (input, sizeof(input), stdin);
       
  1326 
       
  1327         if ((*input == '\n') || (*input == '\0'))   /* Return or Empty */
       
  1328             continue;
       
  1329 
       
  1330         input[strlen (input) - 1] = '\0';   /* remove trailing '\n' */
       
  1331 
       
  1332         if (g_ascii_strcasecmp (input, "s") == 0) {
       
  1333             response = -1;
       
  1334             break;
       
  1335         }else{
       
  1336             gchar *tmp = input;
       
  1337 
       
  1338             for (; ((*tmp >= '0') && (*tmp <= '9') && (*tmp != '\0')); tmp++)
       
  1339                 ;
       
  1340 
       
  1341             if (*tmp != '\0')
       
  1342                 continue;
       
  1343 
       
  1344             response = atoi (input);    /* input belong [0-9]{1, 9}  */
       
  1345 
       
  1346             if ((response < 0) || (response > num_candidate - 1))
       
  1347                 continue;
       
  1348             else
       
  1349                 break;
       
  1350         }
       
  1351     } while (TRUE);
       
  1352 
       
  1353     if (response == -1)
       
  1354         return -1;
       
  1355 
       
  1356     fsexam_encoding_iterate_with_func (encoding_list,
       
  1357                 fsexam_encoding_translate_index,
       
  1358                 &response,
       
  1359                 &result_index);
       
  1360 
       
  1361     return result_index;
       
  1362 }
       
  1363 
       
  1364 /*
       
  1365  *  display_msg func for CLI
       
  1366  */
       
  1367 void
       
  1368 display_msg_default (const gchar *filename, const gchar *msg)
       
  1369 {
       
  1370     if ((filename == NULL) || (msg == NULL))
       
  1371         return;
       
  1372 
       
  1373     if (filename) {
       
  1374         if (g_utf8_validate (filename, -1, NULL)) {
       
  1375             fprintf (stderr, "%s: %s\n", filename, msg ? msg : "");
       
  1376         }else{
       
  1377             gchar *uri = g_filename_to_uri (filename, NULL, NULL);
       
  1378 
       
  1379             if (uri == NULL) {  /* such as filename doesn't exist */
       
  1380                 uri = fsexam_string_escape (filename);
       
  1381             }
       
  1382 
       
  1383             fprintf (stderr, "%s: %s\n", uri, msg ? msg : "");
       
  1384             g_free (uri);
       
  1385         }
       
  1386     }else{ 
       
  1387         fprintf (stderr, "%s\n", msg);
       
  1388     }
       
  1389 
       
  1390     return;
       
  1391 }
       
  1392