diff options
-rw-r--r-- | .perl_checker | 54 | ||||
-rw-r--r-- | Makefile | 66 | ||||
-rw-r--r-- | data/Makefile | 20 | ||||
-rw-r--r-- | data/autostart-draksnapshot.desktop.in | 9 | ||||
-rw-r--r-- | data/gnome-autostart-draksnapshot.desktop.in | 8 | ||||
-rwxr-xr-x | draksnapshot-applet | 269 | ||||
-rw-r--r-- | draksnapshot-applet.xinit | 6 | ||||
-rwxr-xr-x | draksnapshot-config | 308 | ||||
-rwxr-xr-x | draksnapshot-restore | 32 | ||||
-rw-r--r-- | draksnapshot.spec | 60 | ||||
-rw-r--r-- | icons/draksnapshot-big.png | bin | 0 -> 4754 bytes | |||
-rw-r--r-- | icons/draksnapshot.png | bin | 0 -> 1353 bytes | |||
-rw-r--r-- | lib/.perl_checker.cache | bin | 0 -> 3212038 bytes | |||
l--------- | lib/MDV/Snapshot/.perl_checker | 1 | ||||
-rw-r--r-- | lib/MDV/Snapshot/Common.pm | 47 | ||||
-rw-r--r-- | lib/MDV/Snapshot/Restore.pm | 210 | ||||
-rw-r--r-- | po/Makefile | 41 | ||||
-rw-r--r-- | po/POTFILES.in | 2 | ||||
-rw-r--r-- | po/draksnapshot.pot | 206 | ||||
-rw-r--r-- | po/fr.po | 204 |
20 files changed, 1543 insertions, 0 deletions
diff --git a/.perl_checker b/.perl_checker new file mode 100644 index 0000000..c105aa3 --- /dev/null +++ b/.perl_checker @@ -0,0 +1,54 @@ +AutoLoader +base +Carp::Heavy +Compress::Zlib +Config::IniFiles +constant +Cwd +Date::Manip +Digest::base +Digest::Perl::MD5 +Discover +Encode +encoding +feature +File::Basename +File::Find +File::FnMatch +File::GlobMapper +File::MimeInfo::Magic +File::Path +File::Temp +Gtk2::Gdk::Keysyms +Gtk2::Pango +HTTP::Message +HTTP::Request +HTTP::Request::Common +install::gtk +install::pkgs +interactive::http +IO::Compress::Gzip +IO::Handle +IO::Socket::SSL +IO::Uncompress::Gunzip +LWP::UserAgent +LWP::UserlAgent +Net::DNS +Net::HTTP +Net::HTTP::Methods +Net::Ping +Net::SSL +Packdrakeng +RS::Handy +Scalar::Util +SOAP::Lite +Switch +Term::ReadKey +Text::ParseWords +Time::ZoneInfo +URI::Escape +urpm +urpm::cfg +urpm::download +urpm::prompt +warnings diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ab8fc1c --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +PACKAGE = draksnapshot +VERSION:=0.1 + +NAME = draksnapshot +SUBDIRS = data po + +PREFIX = / +DATADIR = $(PREFIX)/usr/share +ICONSDIR = $(DATADIR)/icons +PIXDIR = $(DATADIR)/$(NAME) +SBINDIR = $(PREFIX)/usr/sbin +BINDIR = $(PREFIX)/usr/bin +SYSCONFDIR = $(PREFIX)/etc/sysconfig +SBINREL = ../sbin +PERL_VENDORLIB=$(DESTDIR)/$(shell perl -V:installvendorlib | perl -pi -e "s/.*=//; s/[;']//g") + +localedir = $(PREFIX)/usr/share/locale + +override CFLAGS += -DPACKAGE=\"$(NAME)\" -DLOCALEDIR=\"$(localedir)\" + +all: + for d in $(SUBDIRS); do ( cd $$d ; make $@ ) ; done + +clean: + $(MAKE) -C po $@ + rm -f core .#*[0-9] + for d in $(SUBDIRS); do ( cd $$d ; make $@ ) ; done + find . -name '*~' | xargs rm -f + +install: + install -d $(PREFIX)/usr/{sbin,bin,share/{mime/packages,$(NAME)/pixmaps,autostart,gnome/autostart,icons/{mini,large}}} + install -m755 draksnapshot-config $(SBINDIR) + install -m755 draksnapshot-applet $(BINDIR) + install -d $(SYSCONFDIR) + install -m644 icons/$(NAME).png $(ICONSDIR)/mini/$(NAME).png + install -m644 icons/$(NAME).png $(ICONSDIR)/$(NAME).png + install -m644 icons/$(NAME)-big.png $(ICONSDIR)/large/$(NAME).png + install -m644 icons/*.png $(PIXDIR)/pixmaps + for d in $(SUBDIRS); do make -C $$d $@; done + mkdir -p $(PREFIX)/etc/X11/xinit.d + install -m 755 draksnapshot-applet.xinit $(PREFIX)/etc/X11/xinit.d/ + mkdir -p $(PERL_VENDORLIB)/MDV/Snapshot + install -m 644 lib/MDV/Snapshot/*.pm $(PERL_VENDORLIB)/MDV/Snapshot + +cleandist: + rm -rf $(PACKAGE)-$(VERSION) ../$(PACKAGE)-$(VERSION).tar.bz2 + +localcopy: clean + mkdir ../$(NAME)-$(VERSION) + cp -a . ../$(NAME)-$(VERSION) + mv ../$(NAME)-$(VERSION) . + #FIXME svn export -q -rBASE . $(NAME)-$(VERSION) + +tar: + tar cvfY ../$(PACKAGE)-$(VERSION).tar.lzma $(PACKAGE)-$(VERSION) + rm -rf $(PACKAGE)-$(VERSION) + +dist: cleandist localcopy tar + +log:changelog + +changelog: ../common/username +#svn2cl is available in our contrib. + svn2cl --authors ../common/username.xml --accum + rm -f ChangeLog.bak + svn commit -m "Generated by svn2cl the `date '+%c'`" ChangeLog diff --git a/data/Makefile b/data/Makefile new file mode 100644 index 0000000..a3206e9 --- /dev/null +++ b/data/Makefile @@ -0,0 +1,20 @@ +DATADIR = $(PREFIX)/usr/share + +DESKTOP_IN := $(wildcard *.desktop.in) +DESKTOP = $(DESKTOP_IN:.desktop.in=.desktop) + +all: $(DESKTOP) + +clean: + rm -f $(DESKTOP) + +install: all + mkdir -p $(DATADIR)/autostart + install autostart-draksnapshot.desktop $(DATADIR)/autostart/ + mkdir -p $(DATADIR)/gnome/autostart + install gnome-autostart-draksnapshot.desktop $(DATADIR)/gnome/autostart/ + + +%.desktop: %.desktop.in + intltool-merge --utf8 ../po $< $@ -d -u -c intltool-merge-cache + diff --git a/data/autostart-draksnapshot.desktop.in b/data/autostart-draksnapshot.desktop.in new file mode 100644 index 0000000..083691c --- /dev/null +++ b/data/autostart-draksnapshot.desktop.in @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=DrakSnapshot Applet +Comment=Applet for DrakSnapshot +Exec=%{_bindir}/draksnapshot-applet +Icon=draksnapshot-applet +Type=Application +StartupNotify=true +Categories=X-MandrivaLinux-System-Configuration-Networking;Settings;Network; +X-KDE-autostart-after=kdesktop diff --git a/data/gnome-autostart-draksnapshot.desktop.in b/data/gnome-autostart-draksnapshot.desktop.in new file mode 100644 index 0000000..f61e1e8 --- /dev/null +++ b/data/gnome-autostart-draksnapshot.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=DrakSnapshot Applet +Comment=Applet for DrakSnapshot +Exec=%{_bindir}/draksnapshot-applet +Icon=draksnapshot-applet +Type=Application +StartupNotify=true +Categories=X-MandrivaLinux-System-Configuration-Networking;Settings;Network; diff --git a/draksnapshot-applet b/draksnapshot-applet new file mode 100755 index 0000000..60bf89a --- /dev/null +++ b/draksnapshot-applet @@ -0,0 +1,269 @@ +#!/usr/bin/perl +################################################################################ +# Backup Applet # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + +use strict; +use POSIX ":sys_wait_h"; +use Config; +use lib qw(/usr/lib/libDrakX); +use standalone; # for explanations +use interactive; +use common; +use run_program; +use detect_devices; +use feature 'state'; + +use Gtk2::Pango; + +BEGIN { unshift @::textdomains, 'draksnapshot' } + +use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version +use ugtk2 qw(:all); +use lib qw(/usr/lib/libDrakX/drakfirsttime); +use Gtk2::Notify '-init', 'draksnapshot'; + +ugtk2::add_icon_path("/usr/share/draksnapshot/pixmaps/"); + +my ($menu, $timeout, @discs); + +my $localfile = "$ENV{HOME}/.draksnapshot"; + +my $insensitive_while_running_a_child; + +my %state = ( + + okay => { + menu => [], + do_not_use_bubble => 1, + }, + disk_found => { + menu => [ 'configure' ], + tt => [ N_("USB discs are availlable for backups") ] + }, + config_in_progress => { + menu => [], + do_not_use_bubble => 1, + tt => [ N_("Configurator is currently running") ] + }, + ); + + +my %actions = ( + 'configure' => { name => N("Configure"), launch => \&configure } +); + +my $icon = Gtk2::StatusIcon->new; +$icon->signal_connect(popup_menu => sub { + my ($_icon, $button, $time) = @_; + $menu and $menu->popup(undef, undef, undef, undef, $button, $time); + }); +$icon->signal_connect(activate => \&configure); +my ($opt) = @ARGV; +if ($opt eq '--force' || $opt eq '-f') { setAutoStart('TRUE') } + +shouldStart() or die "$localfile should be set to TRUE: please use --force or -f option to launch applet\n"; + +go2State('okay'); +gtkflush(); +silentCheck(); +Glib::Timeout->add(4000, sub { silentCheck(); 0 }); +setup_cyclic_check(); + + +$SIG{USR1} = 'IGNORE'; +$SIG{USR2} = 'IGNORE'; +$SIG{CHLD} = \&harvester; + +run_program::raw({ detach => 1 }, 'ionice', '-p', $$, '-n7'); +Gtk2->main; + +ugtk2::exit(0); + +my $config_pid; + +# Signal management +sub harvester { + my ($_signame, $_clean) = @_; + my ($childpid, @pids); + my $configurator_returned; + do { + $childpid = waitpid(-1, &WNOHANG); + if ($config_pid && $config_pid == $childpid) { + undef $config_pid; + $configurator_returned = 1; + # we should better check it has really been configured indeed. + setAutoStart('FALSE'); + ugtk2::exit(0); + } + push @pids, $childpid; + WIFEXITED($?) and refresh_gui(1); + } while $childpid > 0; + Glib::Timeout->add(200, sub { silentCheck(); 0 }) if $configurator_returned; + return @pids; +} + +sub fork_exec { + my $pid = run_program::raw({ detach => 1 }, @_); + refresh_gui(1); + return $pid; +} + +sub refresh_gui { + my ($sens) = @_; + #!$conf_launched and silentCheck(); $conf_launched = 0; + my $w = $::main_window ? $::main_window->window : undef; + $insensitive_while_running_a_child = !$sens; + $sens ? gtkset_mousecursor_normal($w) : gtkset_mousecursor_wait($w); + gtkflush(); +} + +sub configure() { + $config_pid = fork_exec('/usr/sbin/draksnapshot-config'); + go2State('config_in_progress'); +} + +sub silentCheck() { + return if $config_pid; + my $w = $::main_window ? $::main_window->window : undef; + gtkset_mousecursor_wait($w); + + # are there any updates ? + + if (@discs = grep { $_->{usb_bus} } detect_devices::hds()) { + go2State('disk_found'); + } + gtkset_mousecursor_normal($w) if !$insensitive_while_running_a_child; +} + +sub setup_cyclic_check() { + $timeout = Glib::Timeout->add( + 5*60 * 1000, + sub { # default to 5 minutes + my $first_run if 0; + if (!$first_run) { + $first_run = 1; + return 1; + } + silentCheck(); + 1; + }); +} + +sub go2State { + my $state = shift; + $menu->destroy if $menu; + $menu = setState($state); +} + +sub isNetwork() { + require network::network; + require network::tools; + my $net = {}; + network::network::read_net_conf($net); + my ($_gw_intf, $_is_up, $gw_address, $_dns_server) = network::tools::get_internet_connection($net); + to_bool($gw_address); +} + +sub shouldStart() { + my %p = getVarsFromSh($localfile); + to_bool($p{AUTOSTART} ne 'FALSE'); +} + +sub setState { + my ($state_type) = @_; + my $checkme; + my $arr = $state{$state_type}{menu}; + state $pixbuf; + if (!$pixbuf) { + $pixbuf = gtkcreate_pixbuf('draksnapshot'); + $icon->set_from_pixbuf($pixbuf); + } + $icon->set_tooltip(formatAlaTeX(translate($state{$state_type}{tt}[0]))); + $icon->set_visible($state_type ne 'okay'); + gtkflush(); # so that bubbles are displayed on right icon + + if ($state{$state_type}{tt}[0] && $icon->isa('Gtk2::StatusIcon') && !$state{$state_type}{do_not_use_bubble}) { + my $bubble = Gtk2::Notify->new_with_status_icon(N("Warning"), formatAlaTeX(translate($state{$state_type}{tt}[0])) . "\n", + '/usr/share/icons/draksnapshot.png', $icon); + $bubble->set_timeout(5000); + $bubble->show; + } + + my $menu = Gtk2::Menu->new; + foreach (@$arr) { + $menu->append(gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label($actions{$_}{name})), activate => $actions{$_}{launch})); + } + $menu->append(gtkshow(Gtk2::SeparatorMenuItem->new)); + $menu->append( + gtksignal_connect( + gtkshow(Gtk2::MenuItem->new_with_label(N("About..."))), + activate => sub { + my $ver = 1; # automatically set from spec file + my $w = gtknew('AboutDialog', name => N("DrakSnapshot %s", $ver), + copyright => N("Copyright (C) %s by Mandriva", '2001-2007'), + license => join('', cat_('/usr/share/common-licenses/GPL')), + icon => '/usr/share/icons/mini/draksnapshot.png', + comments => N("DrakSnapshot enables to backup your machine through periodic snapshots."), + website => 'http://www.mandriva.com', + website_label => N("Mandriva WebSite"), + authors => 'Thierry Vignaud <vignaud@mandriva.com>', + translator_credits => + #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") + N("_: Translator(s) name(s) & email(s)\n"), + transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent', + ); + + $w->show_all; + $w->run; + return 1; + + })); + $menu->append(gtksignal_connect(gtkset_active($checkme = Gtk2::CheckMenuItem->new_with_label(N("Always launch on startup")), shouldStart()), toggled => sub { setAutoStart(uc(bool2text($checkme->get_active))) })); + $checkme->show; + $menu->append(gtksignal_connect(gtkshow(Gtk2::MenuItem->new_with_label(N("Quit"))), activate => sub { mainQuit() })); + $menu; + +} +sub logIt { + my $log = shift; + log::explanations($log); +} + +sub setVar { + my ($file, $var, $st) = @_; + my %s = getVarsFromSh($file); + $s{$var} = $st; + setVarsInSh($file, \%s); +} + +sub setAutoStart { + my ($state) = @_; + if (-f $localfile) { + setVar($localfile, 'AUTOSTART', $state); + } else { + output_p($localfile, "AUTOSTART=$state\n"); + } +} + +sub mainQuit() { + Glib::Source->remove($timeout) if $timeout; + Gtk2->main_quit; +} diff --git a/draksnapshot-applet.xinit b/draksnapshot-applet.xinit new file mode 100644 index 0000000..598cbcf --- /dev/null +++ b/draksnapshot-applet.xinit @@ -0,0 +1,6 @@ +#!/bin/sh +DESKTOP=$1 +case $DESKTOP in + IceWM|Fluxbox|xfce4) exec /usr/bin/draksnapshot-applet;; +esac +EOF diff --git a/draksnapshot-config b/draksnapshot-config new file mode 100755 index 0000000..18ca4ce --- /dev/null +++ b/draksnapshot-config @@ -0,0 +1,308 @@ +#!/usr/bin/perl +################################################################################ +# Backup Configuration Tool # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + + +use lib qw(/usr/lib/libDrakX); +use standalone; #- warning, standalone must be loaded very first, for 'explanations' +use common; +use interactive; +use MDV::Snapshot::Common; +use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version +use ugtk2 qw(:create :dialogs :helpers :wrappers); +use Gtk2::SimpleList; +use interactive; + +require_root_capability(); + +ugtk2::add_icon_path("/usr/share/draksnapshot/pixmaps/"); + +######### read config + +my @ordered_intervals = qw(hourly daily weekly monthly); + +my ($backup_list, $exclude_list) = map { + my $key = $_; + my $list = Gtk2::SimpleList->new('' => 'text'); + # properly size when not embedded: + $list->set_size_request($::isEmbedded ? -1 : 500, 100); + $list->set_headers_visible(0); + @{$list->{data}} = map { [ (split(/\s/, $_, 3))[1] ] } grep { /^$key\s/ } cat_($config_file); + $list; +} qw(backup exclude); + + +my %default_intervals = ( + map { + if (my ($type, $interval) = /^interval\s*(\S*)\s*(\S*)/) { + $type => $interval; + } + } grep { /^interval\s/ } cat_($config_file) +); + +# initialize commented out fields: +$default_intervals{$_} ||= undef foreach @ordered_intervals; + + +######### GUI + +$ugtk2::wm_icon = "draksnapshot-big"; +my $my_win = ugtk2->new(N("Backup snapshots configuration")); + +unless ($::isEmbedded) { + $my_win->{window}->set_border_width(5); + #$my_win->{window}->set_default_size(540,460); +} +$my_win->{window}->signal_connect(delete_event => \&quit); + + +### menus definition +# the menus are not shown +# but they provides shiny shortcut like C-q +my @menu_items = ( + { path => N("/_File"), item_type => '<Branch>' }, + { path => N("/File/_Quit"), accelerator => N("<control>Q"), callback => \&quit }, + { path => N("/_Help"), item_type => '<LastBranch>' }, + { path => N("/Help/_About...") } +); +my $_menubar = $::isEmbedded ? create_factory_menu($my_win->{rwindow}, @menu_items) : undef; +######### menus end + + +my %interval_titles = ( + 'hourly' => N("Hourly interval"), + 'daily' => N("Daily interval"), + 'weekly' => N("Weekly interval"), + 'monthly' => N("Monthly interval"), +); + + +my (%entries, $where); + +gtkadd($my_win->{window}, + gtknew('VBox', children => [ + if_(!$::isEmbedded, 0, Gtk2::Banner->new('draksnapshot-big', + N("Backup snapshots configuration"))), + 0, gtknew('Title1', label => N("Settings")), + 0, gtknew('HBox', spacing => 5, children => [ + 0, gtknew('Label_Left', text => N("Where to backup")), + 1, $where = + gtknew('Entry', text => $backup_directory), + 0, gtknew('Button', text => N("Browse"), + clicked => sub { + my $file_dlg; + $file_dlg = + gtknew('FileSelection', title => N("Path selection"), + ok_button => { + clicked => sub { + $where->set_text($file_dlg->get_filename); + $file_dlg->destroy; + }, + }, + cancel_button => { + clicked => sub { $file_dlg->destroy }, + }, + ); + $file_dlg->set_transient_for($my_win->{real_window}); + $file_dlg->set_modal(1); + $file_dlg->set_filename($where->get_text); + $file_dlg->show; + }, + ), + ]), + + 0, gtknew('Title2', label => N("Intervals")), + 0, gtknew('Table', col_spacings => 10, row_spacings => 5, homogeneous => 1, children => [ + map { + [ + gtknew('Label_Left', text => $interval_titles{$_}), + $entries{$_} = gtknew('Entry', text => $default_intervals{$_}) + ]; + } @ordered_intervals + ]), + 0, gtknew('Title2', label => N("Backup list")), + 1, format_list($backup_list, 1), + 0, gtknew('Title2', label => N("Exclude list")), + 1, format_list($exclude_list), + 0, gtknew('HButtonBox', layout => 'end', border_width => 5, spacing => 5, children_loose => [ + gtknew('Button', text => N("Apply"), clicked => \&save), + gtknew('Button', text => $::isEmbedded ? N("Cancel") : N("Close"), clicked => sub { save(); quit() }) + ]) + ]) + ); + +$my_win->{window}->show_all; +$my_win->main; + + +######### callbacks & helpers + +sub format_list { + my ($list, $o_check) = @_; + gtknew('HBox', + children => [ + 0, gtkset_size_request(Gtk2::Alignment->new(0, 0, 0, 0), 35, 1), + 1, gtknew('ScrolledWindow', child => $list), + 0, gtknew('VBox', border_width => 5, spacing => 5, + children_tight => [ + # FIXME: add "up" & "down" buttons? "edit" button? + gtknew('Button', text => N("Add"), clicked => sub { + add($list, $o_check); + }), + gtknew('Button', text => N("Remove"), clicked => sub { + my ($tree, $iter) = $list->get_selection->get_selected; + return if !$iter; + #my $removed_idx = $tree->get($iter, 5); + $tree->remove($iter); + #sensitive_buttons(0); + #$modified++; + }), + ]), + ], + ); +} + +sub quit() { ugtk2->exit(0) } + + +sub add { + my ($list, $check, $o_iter) = @_; + my $model = $list->get_model; + my $dlg = gtknew('Dialog', transient_for => $my_win->{real_window}, title => N("Add")); + my $browse = gtknew('Button', text => N("browse")); + my $file = gtknew('Entry', $o_iter ? (text => $model->get($o_iter, 1)) : ()); + + my $alrd_exsts = defined $o_iter; + + $browse->signal_connect(clicked => sub { + my $file_dlg = Gtk2::FileSelection->new(N("Path selection")); + $file_dlg->set_modal(1); + $file_dlg->set_transient_for($dlg); + $file_dlg->show; + $file_dlg->set_filename($file->get_text); + $file_dlg->cancel_button->signal_connect(clicked => sub { $file_dlg->destroy }); + $file_dlg->ok_button->signal_connect(clicked => sub { + $file->set_text($file_dlg->get_filename); + $file_dlg->destroy; + }); + }); + + gtkpack_($dlg->vbox, + 0, gtknew('Title2', label => N("Path")), + 0, gtknew('HBox', border_width => 18, children => [ + 1, $file, + 0, $browse + ]), + ); + + #$dlg->set_has_separator(0); + + gtkadd($dlg->action_area, + create_okcancel(my $w = + { + cancel_clicked => sub { $dlg->destroy }, + ok_clicked => sub { + my $path = $file->get_text; + if ($check && $path !~ m!^/!) { + err_dialog(N("Warning"), + N("The first character of the path must be a slash (\"/\"):\n\"%s\"", $path)); + return 1; + } + # create new item if needed (that is when adding a new one) at end of list + if (!$o_iter) { + push @{$list->{data}}, $path; + } + $dlg->destroy; + } + }, + ), + ); + + $w->{ok}->set_sensitive(!$model->get($o_iter, 0)) if $alrd_exsts; + $dlg->show_all; + +} + +sub save() { + save_keyword('interval', map { + my $val = $entries{$_}->get_text; + if_($val, join("\t", 'interval', $_, $val)); + } @ordered_intervals); + + save_keyword('backup', map { join("\t", 'backup', @$_[0], 'localhost/') } @{$backup_list->{data}}); + save_keyword('exclude', map { join("\t", 'exclude', @$_[0]) } @{$exclude_list->{data}}); + save_keyword('snapshot_root', join("\t", 'snapshot_root', $where->get_text)); + generate_cron_entry(); +} + +sub save_keyword { + my ($keyword, @values) = @_; + my ($removed, $done); + my $new_val = join('', map { "$_\n" }@values); + substInFile { + if (/^$keyword/) { + undef $_; + $removed++; + } + if ($removed == 1 && !$done) { + $done++; + $_ .= $new_val; + } + } $config_file; + # be safe: + append_to_file($config_file, $new_val) if !$done; +} + + + +sub generate_cron_entry() { + my %minutes = ( + 'hourly' => 0, + 'daily' => 50, + 'weekly' => 40, + 'monthly' => 30, + ); + + my $cron_file = "$::prefix/etc/cron.d/rsnapshot"; + + output($cron_file, + qq(# WARNING: This file is autogenerated from /etc/rsnapshot.conf. +# WARNING: Please alter /etc/rsnapshot.conf instead of $cron_file +# +# $cron_file: crontab fragment for rsnapshot + +), + (map { + if (my ($type, $interval) = /^interval\s*(\S*)\s*(\S*)/) { + # crontab: minute hour day_month month day_of_week user command + join("\t", + $minutes{$type}, + ($type eq 'hourly' ? "*/" . 24/$interval : 4), + ($type eq 'monthly' ? 1 : '*'), + '*' , + ($type eq 'weekly' ? 6 : '*'), + 'root', + "/usr/bin/rsnapshot $type" + ), "\n"; + } + } grep { /^interval\s/ } cat_($config_file)), + ); +} diff --git a/draksnapshot-restore b/draksnapshot-restore new file mode 100755 index 0000000..d59ec21 --- /dev/null +++ b/draksnapshot-restore @@ -0,0 +1,32 @@ +#!/usr/bin/perl +################################################################################ +# Backup Configuration Tool # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + + +use lib qw(/usr/lib/libDrakX); +use standalone; #- warning, standalone must be loaded very first, for 'explanations' +use interactive; +use MDV::Snapshot::Restore; + +my $in = interactive->vnew('su'); +$ugtk2::wm_icon = "logdrake"; + +MDV::Snapshot::Restore::main($in); diff --git a/draksnapshot.spec b/draksnapshot.spec new file mode 100644 index 0000000..0340958 --- /dev/null +++ b/draksnapshot.spec @@ -0,0 +1,60 @@ +Summary: Snapshot utility +Name: draksnapshot +Version: 0.1 +Release: %mkrel 1 +Source0: %{name}-%{version}.tar.lzma +URL: http://www.mandrivalinux.com +License: GPL +Group: Archiving/Other +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +Requires: rsnapshot +BuildRequires: gettext, perl-MDK-Common-devel +BuildArch: noarch + + +%description +This is a backup program that uses rsync to take backup snapshots of +filesystems. It uses hard links to save space on disk. + +%prep +%setup -q + +%build +%make +perl -pi -e 's!my \$ver = 1;!my \$ver = '"'%version-%release'"';!' draksnapshot-applet + +%install +rm -rf %{buildroot} +%makeinstall_std PREFIX=$RPM_BUILD_ROOT + +%{find_lang} %{name} + +%post +%{update_desktop_database} +%{update_mime_database} + +%postun +%{clean_desktop_database} +%{clean_mime_database} + +%clean +rm -rf %{buildroot} + +%files -f %name.lang +%defattr(-,root,root) +%defattr(-,root,root) +%_bindir/* +%_datadir/%{name} +%_datadir/autostart/autostart-draksnapshot.desktop +%_datadir/gnome/autostart/gnome-autostart-draksnapshot.desktop +%_iconsdir/*.png +%_liconsdir/*.png +%_miconsdir/*.png +%_sbindir/* +%_sysconfdir/X11/xinit.d/draksnapshot-applet.xinit +%perl_vendorlib/MDV + + +%changelog +* Wed Feb 27 2008 Thierry Vignaud <tvignaud@mandriva.com> 0.1-1mdv2008.1 +- initial release diff --git a/icons/draksnapshot-big.png b/icons/draksnapshot-big.png Binary files differnew file mode 100644 index 0000000..699ebd8 --- /dev/null +++ b/icons/draksnapshot-big.png diff --git a/icons/draksnapshot.png b/icons/draksnapshot.png Binary files differnew file mode 100644 index 0000000..eae6250 --- /dev/null +++ b/icons/draksnapshot.png diff --git a/lib/.perl_checker.cache b/lib/.perl_checker.cache Binary files differnew file mode 100644 index 0000000..9c02a4c --- /dev/null +++ b/lib/.perl_checker.cache diff --git a/lib/MDV/Snapshot/.perl_checker b/lib/MDV/Snapshot/.perl_checker new file mode 120000 index 0000000..6246b2f --- /dev/null +++ b/lib/MDV/Snapshot/.perl_checker @@ -0,0 +1 @@ +../../../.perl_checker
\ No newline at end of file diff --git a/lib/MDV/Snapshot/Common.pm b/lib/MDV/Snapshot/Common.pm new file mode 100644 index 0000000..3ef2d63 --- /dev/null +++ b/lib/MDV/Snapshot/Common.pm @@ -0,0 +1,47 @@ +package MDV::Snapshot::Common; + +################################################################################ +# Backup Configuration Tool # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + + +use lib qw(/usr/lib/libDrakX); +use common; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw($config_file $backup_directory); + +our ($config_file, $backup_directory); + +sub init { + $config_file = "$::prefix/etc/rsnapshot.conf"; + + $backup_directory = "$::prefix/" . + (split(/\s/, (find { /^snapshot_root\s/ } cat_($config_file)), 2))[1]; + + # normalize for glob(): + chomp($backup_directory); + $backup_directory =~ s!/*$!!; +} + +init(); + +1; diff --git a/lib/MDV/Snapshot/Restore.pm b/lib/MDV/Snapshot/Restore.pm new file mode 100644 index 0000000..0a2a83c --- /dev/null +++ b/lib/MDV/Snapshot/Restore.pm @@ -0,0 +1,210 @@ +package MDV::Snapshot::Restore; + +################################################################################ +# Backup Configuration Tool # +# # +# Copyright (C) 2008 Mandriva # +# # +# Thierry Vignaud <tvignaud at mandriva dot com> # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + + +use lib qw(/usr/lib/libDrakX); +use c; +use locale; +use MDV::Snapshot::Common; +use common; +use interactive; +use POSIX; +use run_program; +use fsedit; +use fs::type; +use fs; +use any; + +sub get_fstab() { + my $all_hds = fsedit::get_hds(); + fs::get_raw_hds('', $all_hds); + fs::get_info_from_fstab($all_hds); + [ fs::get::fstab($all_hds) ]; # $fstab +} + +sub get_handle { + my ($fstab, $part, $o_rw) = @_; + my ($mntpnt) = map { $_->{mntpoint} } grep { $_->{device} eq $part->{device} } @$fstab; + + # do not bother failed to mount with ro b/c already mounted: + $mntpnt ? { dir => $mntpnt } : any::inspect($part, $::prefix, $o_rw); +} + + +sub find_disks_with_rsnapshot { + my ($fstab) = @_; + + my @true_fs = + grep { isTrueLocalFS($_) && + !member($_->{mntpoint}, + '/home', grep { $_ ne '/' } fs::type::directories_needed_to_boot()); + } @$fstab; + + log::l("looking for rsnapshot.conf on partitions " . join(' ', map { $_->{device} } @true_fs)); + + my @found; + foreach my $part (@true_fs) { + my $handle = get_handle($fstab, $part); + next if !$handle; + next if ! -e "$handle->{dir}/etc/rsnapshot.conf"; + + if (my $f = common::release_file($handle->{dir})) { + my $h = common::parse_release_file($handle->{dir}, $f, $part); + $h->{name} = $h->{release}; + push @found, $h; + } + } + + return @found; +} + +sub get_snapshots() { + my %backups = + map { + my $backup = $_; + my ($type, $_number) = m!$backup_directory/([^/]*)\.([0-9]*)!; + my $raw_date = [ stat($backup) ]->[-4]; + my $date = POSIX::strftime($type eq 'hourly' ? "%c" : "%x", localtime($raw_date)); + c::set_tagged_utf8($date); + $backup => { raw_date => $raw_date, date => $date, backup => $backup, type => $type }; + } glob_("$backup_directory/*"); + %backups; +} + +sub configure { + my ($in) = @_; + my $backup; + my %backups = get_snapshots(); + + if (!%backups) { + $in->ask_warn(N("Error"), N("No backup found!")); + return; + } + + my %i18n = ( + 'hourly' => N("Hourly"), + 'daily' => N("Daily"), + 'weekly' => N("Weekly"), + 'monthly' => N("Monthly"), + ); + + $in->ask_from_( + { + title => N("Backup snapshots configuration"), + }, + [ + { title => 1, label => N("Backup snapshots configuration") }, + { label => N("Please select the backup you want to restore.") }, + { val => \$backup, sort => 0, allow_empty_list => 1, + format => sub { + #-PO: eg: "Daily snapshot done on Sun Feb 4 + N("%s snapshot done on %s", $i18n{$backups{$_[0]}{type}}, $backups{$_[0]}{date}); + }, + list => [ sort { $backups{$a}{raw_date} cmp $backups{$b}{raw_date} } keys %backups ], + }, + ]) or return; + + my $_wait = $in->wait_message(N("Please wait"), N("Restoring backup in progress")); + my $backup_dir = $backups{$backup}{backup}; + # FIXME: add a progress bar + foreach (glob_("$backup_dir/localhost/*")) { + my $dir = $_; + s!^$backup_dir/localhost!!; + log::l("would run system('rsync', '-vrlpt', $dir, $::prefix$_)\n") if $::testing; + # FIXME: should we use --delete? + run_program::run('rsync', '-vrlpt', $dir, "$::prefix$_") if !$::testing; + } +} + + +sub mount_all { + my ($in, $fstab) = @_; + local $::testing = 0; + + my @errs; + foreach my $part (sort { $a->{mntpoint} cmp $b->{mntpoint} } + grep { $_->{mntpoint} && maybeFormatted($_) && $_->{device} ne 'none' + && !member($_->{fs_type}, 'swap', 'nfs', if_($::isInstall, 'ntfs')); + } + @$fstab) { + + eval { fs::mount::part($part) }; + push @errs, $@ if $@; + } + $in->ask_warn(N("Warning"), N("mount failled:") . join("\n", '', @errs)) if @errs; +} + +sub main { + my ($in) = @_; + + my ($system, $fstab, @found); + refresh: + $fstab = get_fstab(); + @found = find_disks_with_rsnapshot($fstab); + + $in->ask_from_({ interactive_help_id => 'configureX_chooser', + title => N("Restoring Backup"), + messages => join("\n", + N("Please select the partition of the system to restore."), + N("Do not forgot to plug the USB disk that contains your backups."), + ) + }, + [ + { label => N("System to restore"), }, + { + val => \$system, list => \@found, allow_empty_list => 1, + format => sub { + my $dev = $_[0]; + N("\"%s\" (on %s)", $dev->{name}, $dev->{part}{device}); + }, + }, + { + val => N("Refresh"), clicked => sub { goto refresh }, + }, + ]); + + my $handle = get_handle($fstab, $system->{part}, 1); + local $::prefix = $handle->{dir}; + + # reinit paths according to temp mount point: + MDV::Snapshot::Common::init(); + + my $ch_fstab; + if (ref($handle) =~ /before_leaving/) { + $ch_fstab = [ fs::read_fstab($::prefix, '/etc/fstab') ]; + mount_all($in, $ch_fstab); + } + + configure($in); + + # make sure everything is umounted: + if (ref($handle) =~ /before_leaving/) { + + # reread for {real_mountpoint}: + $ch_fstab = [ fs::read_fstab($::prefix, '/etc/fstab') ]; + + eval { fs::mount::umount_all($ch_fstab) }; + } +} + +1; diff --git a/po/Makefile b/po/Makefile new file mode 100644 index 0000000..eb2e9c1 --- /dev/null +++ b/po/Makefile @@ -0,0 +1,41 @@ +# Installation directories +localedir = $(PREFIX)/usr/share/locale + +PGOAL = draksnapshot + +top_srcdir=.. + +PL_FILES = ../draksnapshot-applet ../draksnapshot-config ../draksnapshot-restore + +POFILES = $(shell ls *.po) + +POFILES = $(shell ls *.po) +MOFILES = $(POFILES:%.po=%.mo) +LANGS = $(POFILES:%.po=%) + +GOALS = $(PGOAL).pot $(MOFILES) + +all: $(GOALS) + +install: all + for l in $(LANGS); do \ + install -d $(localedir)/$$l/LC_MESSAGES; \ + install -m 644 $$l.mo $(localedir)/$$l/LC_MESSAGES/$(PGOAL).mo; \ + done + +clean: + rm -f *~ *.[oas] *.mo TAGS + +%.mo: %.po + msgfmt -o $@ $< + +$(PGOAL).pot: $(PL_FILES) + perl_checker -q --generate-pot $(PGOAL).pot $(PL_FILES) + +merge: $(PGOAL).pot + @for n in $(POFILES); do \ + echo "Merging $$n"; \ + msgmerge "$$n" $< > "$$n"t; \ + mv -f "$$n"t "$$n"; \ + done + diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..2b36578 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,2 @@ +data/autostart-draksnapshot.desktop.in +data/gnome-autostart-draksnapshot.desktop.in diff --git a/po/draksnapshot.pot b/po/draksnapshot.pot new file mode 100644 index 0000000..965c535 --- /dev/null +++ b/po/draksnapshot.pot @@ -0,0 +1,206 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2008-02-27 18:14+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8-bit\n" + +#: ../draksnapshot-applet:59 +#, c-format +msgid "USB discs are availlable for backups" +msgstr "" + +#: ../draksnapshot-applet:64 +#, c-format +msgid "Configurator is currently running" +msgstr "" + +#: ../draksnapshot-applet:70 +#, c-format +msgid "Configure" +msgstr "" + +#: ../draksnapshot-applet:204 ../draksnapshot-config:224 +#, c-format +msgid "Warning" +msgstr "" + +#: ../draksnapshot-applet:217 +#, c-format +msgid "About..." +msgstr "" + +#: ../draksnapshot-applet:220 +#, c-format +msgid "DrakSnapshot %s" +msgstr "" + +#: ../draksnapshot-applet:221 +#, c-format +msgid "Copyright (C) %s by Mandriva" +msgstr "" + +#: ../draksnapshot-applet:224 +#, c-format +msgid "DrakSnapshot enables to backup your machine through periodic snapshots." +msgstr "" + +#: ../draksnapshot-applet:226 +#, c-format +msgid "Mandriva WebSite" +msgstr "" + +#. -PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +#: ../draksnapshot-applet:230 +#, c-format +msgid "" +"_: Translator(s) name(s) & email(s)\n" +"" +msgstr "" + +#: ../draksnapshot-applet:239 +#, c-format +msgid "Always launch on startup" +msgstr "" + +#: ../draksnapshot-applet:241 +#, c-format +msgid "Quit" +msgstr "" + +#: ../draksnapshot-config:68 ../draksnapshot-config:102 +#, c-format +msgid "Backup snapshots configuration" +msgstr "" + +#: ../draksnapshot-config:81 +#, c-format +msgid "/_File" +msgstr "" + +#: ../draksnapshot-config:82 +#, c-format +msgid "/File/_Quit" +msgstr "" + +#: ../draksnapshot-config:82 +#, c-format +msgid "<control>Q" +msgstr "" + +#: ../draksnapshot-config:83 +#, c-format +msgid "/_Help" +msgstr "" + +#: ../draksnapshot-config:84 +#, c-format +msgid "/Help/_About..." +msgstr "" + +#: ../draksnapshot-config:91 +#, c-format +msgid "Hourly interval" +msgstr "" + +#: ../draksnapshot-config:92 +#, c-format +msgid "Daily interval" +msgstr "" + +#: ../draksnapshot-config:93 +#, c-format +msgid "Weekly interval" +msgstr "" + +#: ../draksnapshot-config:94 +#, c-format +msgid "Monthly interval" +msgstr "" + +#: ../draksnapshot-config:103 +#, c-format +msgid "Settings" +msgstr "" + +#: ../draksnapshot-config:105 +#, c-format +msgid "Where to backup" +msgstr "" + +#: ../draksnapshot-config:108 +#, c-format +msgid "Browse" +msgstr "" + +#: ../draksnapshot-config:112 ../draksnapshot-config:195 +#, c-format +msgid "Path selection" +msgstr "" + +#: ../draksnapshot-config:131 +#, c-format +msgid "Intervals" +msgstr "" + +#: ../draksnapshot-config:140 +#, c-format +msgid "Backup list" +msgstr "" + +#: ../draksnapshot-config:142 +#, c-format +msgid "Exclude list" +msgstr "" + +#: ../draksnapshot-config:145 +#, c-format +msgid "Apply" +msgstr "" + +#: ../draksnapshot-config:146 +#, c-format +msgid "Cancel" +msgstr "" + +#: ../draksnapshot-config:146 +#, c-format +msgid "Close" +msgstr "" + +#: ../draksnapshot-config:166 ../draksnapshot-config:188 +#, c-format +msgid "Add" +msgstr "" + +#: ../draksnapshot-config:169 +#, c-format +msgid "Remove" +msgstr "" + +#: ../draksnapshot-config:189 +#, c-format +msgid "browse" +msgstr "" + +#: ../draksnapshot-config:208 +#, c-format +msgid "Path" +msgstr "" + +#: ../draksnapshot-config:225 +#, c-format +msgid "" +"The first character of the path must be a slash (\"/\"):\n" +"\"%s\"" +msgstr "" + diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..a695399 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,204 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: draksnapshot\n" +"POT-Creation-Date: 2008-02-27 18:14+0100\n" +"PO-Revision-Date: 2008-02-27 18:15+0100\n" +"Last-Translator: Thierry Vignaud <tvignaud@mandriva.com>\n" +"Language-Team: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8-bit\n" + +#: ../draksnapshot-applet:59 +#, c-format +msgid "USB discs are availlable for backups" +msgstr "" + +#: ../draksnapshot-applet:64 +#, c-format +msgid "Configurator is currently running" +msgstr "" + +#: ../draksnapshot-applet:70 +#, c-format +msgid "Configure" +msgstr "Configurer" + +#: ../draksnapshot-applet:204 ../draksnapshot-config:224 +#, c-format +msgid "Warning" +msgstr "Attention" + +#: ../draksnapshot-applet:217 +#, c-format +msgid "About..." +msgstr "À propos ..." + +#: ../draksnapshot-applet:220 +#, c-format +msgid "DrakSnapshot %s" +msgstr "" + +#: ../draksnapshot-applet:221 +#, c-format +msgid "Copyright (C) %s by Mandriva" +msgstr "Copyright (C) %s par Mandriva" + +#: ../draksnapshot-applet:224 +#, c-format +msgid "DrakSnapshot enables to backup your machine through periodic snapshots." +msgstr "" + +#: ../draksnapshot-applet:226 +#, c-format +msgid "Mandriva WebSite" +msgstr "" + +#. -PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +#: ../draksnapshot-applet:230 +#, c-format +msgid "_: Translator(s) name(s) & email(s)\n" +msgstr "Thierry Vignaud <tvignaud@mandriva.com>\n" + +#: ../draksnapshot-applet:239 +#, c-format +msgid "Always launch on startup" +msgstr "Toujours lancer au démarrage" + +#: ../draksnapshot-applet:241 +#, c-format +msgid "Quit" +msgstr "Quitter" + +#: ../draksnapshot-config:68 ../draksnapshot-config:102 +#, c-format +msgid "Backup snapshots configuration" +msgstr "" + +#: ../draksnapshot-config:81 +#, c-format +msgid "/_File" +msgstr "/_Fichier" + +#: ../draksnapshot-config:82 +#, c-format +msgid "/File/_Quit" +msgstr "/Fichier/_Quitter" + +#: ../draksnapshot-config:82 +#, c-format +msgid "<control>Q" +msgstr "<control>Q" + +#: ../draksnapshot-config:83 +#, c-format +msgid "/_Help" +msgstr "/_Aide" + +#: ../draksnapshot-config:84 +#, c-format +msgid "/Help/_About..." +msgstr "/Aide/_A propos..." + +#: ../draksnapshot-config:91 +#, c-format +msgid "Hourly interval" +msgstr "" + +#: ../draksnapshot-config:92 +#, c-format +msgid "Daily interval" +msgstr "" + +#: ../draksnapshot-config:93 +#, c-format +msgid "Weekly interval" +msgstr "" + +#: ../draksnapshot-config:94 +#, c-format +msgid "Monthly interval" +msgstr "" + +#: ../draksnapshot-config:103 +#, c-format +msgid "Settings" +msgstr "Paramètres" + +#: ../draksnapshot-config:105 +#, c-format +msgid "Where to backup" +msgstr "" + +#: ../draksnapshot-config:108 +#, c-format +msgid "Browse" +msgstr "Naviguer" + +#: ../draksnapshot-config:112 ../draksnapshot-config:195 +#, c-format +msgid "Path selection" +msgstr "Sélection du chemin" + +#: ../draksnapshot-config:131 +#, c-format +msgid "Intervals" +msgstr "" + +#: ../draksnapshot-config:140 +#, c-format +msgid "Backup list" +msgstr "" + +#: ../draksnapshot-config:142 +#, c-format +msgid "Exclude list" +msgstr "" + +#: ../draksnapshot-config:145 +#, c-format +msgid "Apply" +msgstr "Appliquer" + +#: ../draksnapshot-config:146 +#, c-format +msgid "Cancel" +msgstr "Annuler" + +#: ../draksnapshot-config:146 +#, c-format +msgid "Close" +msgstr "Fermer" + +#: ../draksnapshot-config:166 ../draksnapshot-config:188 +#, c-format +msgid "Add" +msgstr "Ajouter" + +#: ../draksnapshot-config:169 +#, c-format +msgid "Remove" +msgstr "Supprimer" + +#: ../draksnapshot-config:189 +#, c-format +msgid "browse" +msgstr "naviguer" + +#: ../draksnapshot-config:208 +#, c-format +msgid "Path" +msgstr "Chemin" + +#: ../draksnapshot-config:225 +#, c-format +msgid "" +"The first character of the path must be a slash (\"/\"):\n" +"\"%s\"" +msgstr "" +"Le premier caractère du chemin doit être un slash (\\\"/\\\"):\n" +"« %s »" |