(root)/
glib-2.79.0/
gio/
gio-tool-trash.c
       1  /*
       2   * Copyright 2015 Red Hat, Inc.
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General Public
      17   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * Author: Matthias Clasen <mclasen@redhat.com>
      20   */
      21  
      22  #include "config.h"
      23  
      24  #include <gio/gio.h>
      25  #include <gi18n.h>
      26  
      27  #include "gio-tool.h"
      28  
      29  
      30  static gboolean global_force = FALSE;
      31  static gboolean empty = FALSE;
      32  static gboolean restore = FALSE;
      33  static gboolean list = FALSE;
      34  static const GOptionEntry entries[] = {
      35    { "force", 'f', 0, G_OPTION_ARG_NONE, &global_force, N_("Ignore nonexistent files, never prompt"), NULL },
      36    { "empty", 0, 0, G_OPTION_ARG_NONE, &empty, N_("Empty the trash"), NULL },
      37    { "list", 0, 0, G_OPTION_ARG_NONE, &list, N_("List files in the trash with their original locations"), NULL },
      38    { "restore", 0, 0, G_OPTION_ARG_NONE, &restore, N_("Restore a file from trash to its original location (possibly "
      39                                                       "recreating the directory)"), NULL },
      40    G_OPTION_ENTRY_NULL
      41  };
      42  
      43  static void
      44  delete_trash_file (GFile *file, gboolean del_file, gboolean del_children)
      45  {
      46    GFileInfo *info;
      47    GFile *child;
      48    GFileEnumerator *enumerator;
      49  
      50    g_return_if_fail (g_file_has_uri_scheme (file, "trash"));
      51  
      52    if (del_children)
      53      {
      54        enumerator = g_file_enumerate_children (file,
      55                                                G_FILE_ATTRIBUTE_STANDARD_NAME ","
      56                                                G_FILE_ATTRIBUTE_STANDARD_TYPE,
      57                                                G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
      58                                                NULL,
      59                                                NULL);
      60        if (enumerator)
      61          {
      62            while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
      63              {
      64                child = g_file_get_child (file, g_file_info_get_name (info));
      65  
      66                /* The g_file_delete operation works differently for locations
      67                 * provided by the trash backend as it prevents modifications of
      68                 * trashed items. For that reason, it is enough to call
      69                 * g_file_delete on top-level items only.
      70                 */
      71                delete_trash_file (child, TRUE, FALSE);
      72  
      73                g_object_unref (child);
      74                g_object_unref (info);
      75              }
      76            g_file_enumerator_close (enumerator, NULL, NULL);
      77            g_object_unref (enumerator);
      78          }
      79      }
      80  
      81    if (del_file)
      82      g_file_delete (file, NULL, NULL);
      83  }
      84  
      85  static gboolean
      86  restore_trash (GFile         *file,
      87                 gboolean       force,
      88                 GCancellable  *cancellable,
      89                 GError       **error)
      90  {
      91    GFileInfo *info = NULL;
      92    GFile *target = NULL;
      93    GFile *dir_target = NULL;
      94    gboolean ret = FALSE;
      95    gchar *orig_path = NULL;
      96    GError *local_error = NULL;
      97  
      98    info = g_file_query_info (file, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NONE, cancellable, &local_error);
      99    if (local_error)
     100      {
     101        g_propagate_error (error, local_error);
     102        goto exit_func;
     103      }
     104  
     105    orig_path = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
     106    if (!orig_path)
     107      {
     108        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Unable to find original path"));
     109        goto exit_func;
     110      }
     111  
     112    target = g_file_new_for_commandline_arg (orig_path);
     113    g_free (orig_path);
     114  
     115    dir_target = g_file_get_parent (target);
     116    if (dir_target)
     117      {
     118        g_file_make_directory_with_parents (dir_target, cancellable, &local_error);
     119        if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
     120          {
     121            g_clear_error (&local_error);
     122          }
     123        else if (local_error != NULL)
     124          {
     125            g_propagate_prefixed_error (error, local_error, _("Unable to recreate original location: "));
     126            goto exit_func;
     127          }
     128      }
     129  
     130    if (!g_file_move (file,
     131                      target,
     132                      force ? G_FILE_COPY_OVERWRITE : G_FILE_COPY_NONE,
     133                      cancellable,
     134                      NULL,
     135                      NULL,
     136                      &local_error))
     137      {
     138        g_propagate_prefixed_error (error, local_error, _("Unable to move file to its original location: "));
     139        goto exit_func;
     140      }
     141    ret = TRUE;
     142  
     143  exit_func:
     144    g_clear_object (&target);
     145    g_clear_object (&dir_target);
     146    g_clear_object (&info);
     147    return ret;
     148  }
     149  
     150  static gboolean
     151  trash_list (GFile         *file,
     152              GCancellable  *cancellable,
     153              GError       **error)
     154  {
     155    GFileEnumerator *enumerator;
     156    GFileInfo *info;
     157    GError *local_error = NULL;
     158    gboolean res;
     159  
     160    enumerator = g_file_enumerate_children (file,
     161                                            G_FILE_ATTRIBUTE_STANDARD_NAME ","
     162                                            G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
     163                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
     164                                            cancellable,
     165                                            &local_error);
     166    if (!enumerator)
     167      {
     168        g_propagate_error (error, local_error);
     169        return FALSE;
     170      }
     171  
     172    res = TRUE;
     173    while ((info = g_file_enumerator_next_file (enumerator, cancellable, &local_error)) != NULL)
     174      {
     175        const char *name;
     176        char *orig_path;
     177        char *uri;
     178        GFile* child;
     179  
     180        name = g_file_info_get_name (info);
     181        child = g_file_get_child (file, name);
     182        uri = g_file_get_uri (child);
     183        g_object_unref (child);
     184        orig_path = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
     185  
     186        g_print ("%s\t%s\n", uri, orig_path);
     187  
     188        g_object_unref (info);
     189        g_free (orig_path);
     190        g_free (uri);
     191      }
     192  
     193    if (local_error)
     194      {
     195        g_propagate_error (error, local_error);
     196        local_error = NULL;
     197        res = FALSE;
     198      }
     199  
     200    if (!g_file_enumerator_close (enumerator, cancellable, &local_error))
     201      {
     202        print_file_error (file, local_error->message);
     203        g_clear_error (&local_error);
     204        res = FALSE;
     205      }
     206  
     207    return res;
     208  }
     209  
     210  int
     211  handle_trash (int argc, char *argv[], gboolean do_help)
     212  {
     213    GOptionContext *context;
     214    gchar *param;
     215    GError *error = NULL;
     216    int retval = 0;
     217    GFile *file;
     218  
     219    g_set_prgname ("gio trash");
     220  
     221    /* Translators: commandline placeholder */
     222    param = g_strdup_printf ("[%s…]", _("LOCATION"));
     223    context = g_option_context_new (param);
     224    g_free (param);
     225    g_option_context_set_help_enabled (context, FALSE);
     226    g_option_context_set_summary (context,
     227        _("Move/Restore files or directories to the trash."));
     228    g_option_context_set_description (context,
     229        _("Note: for --restore switch, if the original location of the trashed file \n"
     230          "already exists, it will not be overwritten unless --force is set."));
     231    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
     232  
     233    if (do_help)
     234      {
     235        show_help (context, NULL);
     236        g_option_context_free (context);
     237        return 0;
     238      }
     239  
     240    if (!g_option_context_parse (context, &argc, &argv, &error))
     241      {
     242        show_help (context, error->message);
     243        g_error_free (error);
     244        g_option_context_free (context);
     245        return 1;
     246      }
     247  
     248    if (argc > 1)
     249      {
     250        int i;
     251  
     252        for (i = 1; i < argc; i++)
     253          {
     254            file = g_file_new_for_commandline_arg (argv[i]);
     255            error = NULL;
     256            if (restore)
     257              {
     258                if (!g_file_has_uri_scheme (file, "trash"))
     259                  {
     260                    print_file_error (file, _("Location given doesn't start with trash:///"));
     261                    retval = 1;
     262                  }
     263                else if (!restore_trash (file, global_force, NULL, &error))
     264                  {
     265                    print_file_error (file, error->message);
     266                    retval = 1;
     267                  }
     268              }
     269            else if (!g_file_trash (file, NULL, &error))
     270              {
     271                if (!global_force ||
     272                    !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
     273                  {
     274                    print_file_error (file, error->message);
     275                    retval = 1;
     276                  }
     277              }
     278            g_clear_error (&error);
     279            g_object_unref (file);
     280          }
     281      }
     282    else if (list)
     283      {
     284        file = g_file_new_for_uri ("trash:");
     285        trash_list (file, NULL, &error);
     286        if (error)
     287          {
     288            print_file_error (file, error->message);
     289            g_clear_error (&error);
     290            retval = 1;
     291          }
     292        g_object_unref (file);
     293      }
     294    else if (empty)
     295      {
     296        file = g_file_new_for_uri ("trash:");
     297        delete_trash_file (file, FALSE, TRUE);
     298        g_object_unref (file);
     299      }
     300  
     301    if (argc == 1 && !empty && !list)
     302      {
     303        show_help (context, _("No locations given"));
     304        g_option_context_free (context);
     305        return 1;
     306      }
     307  
     308    g_option_context_free (context);
     309  
     310    return retval;
     311  }