From 55e0a2452cbe9870adbd9666a07b0a3e8d2baaeb Mon Sep 17 00:00:00 2001 From: Colin Leroy Date: Tue, 4 Oct 2005 16:01:12 +0000 Subject: [PATCH] 2005-10-04 [colin] 1.9.15cvs7 * src/export.c * src/import.c * src/mbox.c Fix mbox import/export buglets Patch by wwp * src/plugins/trayicon/trayicon.c Add offline swithc Patch by wwp --- ChangeLog-gtk2.claws | 11 +++ PATCHSETS | 1 + configure.ac | 2 +- src/export.c | 10 +- src/import.c | 8 +- src/mbox.c | 156 ++++++++++++++++++-------------- src/plugins/trayicon/trayicon.c | 116 +++++++++++++++--------- 7 files changed, 184 insertions(+), 120 deletions(-) diff --git a/ChangeLog-gtk2.claws b/ChangeLog-gtk2.claws index 42091f4e3..c248ccd04 100644 --- a/ChangeLog-gtk2.claws +++ b/ChangeLog-gtk2.claws @@ -1,3 +1,14 @@ +2005-10-04 [colin] 1.9.15cvs7 + + * src/export.c + * src/import.c + * src/mbox.c + Fix mbox import/export buglets + Patch by wwp + * src/plugins/trayicon/trayicon.c + Add offline swithc + Patch by wwp + 2005-10-04 [colin] 1.9.15cvs6 * src/folder.c diff --git a/PATCHSETS b/PATCHSETS index 17f9c6fbd..b7b4db966 100644 --- a/PATCHSETS +++ b/PATCHSETS @@ -843,3 +843,4 @@ ( cvs diff -u -r 1.149.2.36 -r 1.149.2.37 src/inc.c; cvs diff -u -r 1.105.2.36 -r 1.105.2.37 src/prefs_account.c; cvs diff -u -r 1.1.2.11 -r 1.1.2.12 src/wizard.c; ) > 1.9.15cvs4.patchset ( cvs diff -u -r 1.213.2.58 -r 1.213.2.59 src/folder.c; ) > 1.9.15cvs5.patchset ( cvs diff -u -r 1.213.2.59 -r 1.213.2.60 src/folder.c; cvs diff -u -r 1.207.2.65 -r 1.207.2.66 src/folderview.c; cvs diff -u -r 1.395.2.129 -r 1.395.2.130 src/summaryview.c; ) > 1.9.15cvs6.patchset +( cvs diff -u -r 1.8.2.10 -r 1.8.2.11 src/export.c; cvs diff -u -r 1.13.2.10 -r 1.13.2.11 src/import.c; cvs diff -u -r 1.28.2.10 -r 1.28.2.11 src/mbox.c; cvs diff -u -r 1.14.2.21 -r 1.14.2.22 src/plugins/trayicon/trayicon.c; ) > 1.9.15cvs7.patchset diff --git a/configure.ac b/configure.ac index 56a8a987b..cd6dc2025 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,7 @@ MINOR_VERSION=9 MICRO_VERSION=15 INTERFACE_AGE=0 BINARY_AGE=0 -EXTRA_VERSION=6 +EXTRA_VERSION=7 EXTRA_RELEASE= EXTRA_GTK2_VERSION= diff --git a/src/export.c b/src/export.c index 411c85ff2..74a52ebc6 100644 --- a/src/export.c +++ b/src/export.c @@ -138,6 +138,7 @@ static void export_create(void) GtkWidget *file_label; GtkWidget *src_label; GtkWidget *confirm_area; + GtkWidget *image; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), _("Export")); @@ -187,13 +188,18 @@ static void export_create(void) gtk_table_attach(GTK_TABLE(table), file_entry, 1, 2, 1, 2, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); - src_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + /* XXX GTK 2.6: GTK_STOCK_DIRECTORY */ + src_button = gtk_button_new_with_mnemonic(_("_Browse")); + gtk_button_set_image((GtkButton*)src_button, + gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); gtk_table_attach(GTK_TABLE(table), src_button, 2, 3, 0, 1, 0, 0, 0, 0); g_signal_connect(G_OBJECT(src_button), "clicked", G_CALLBACK(export_srcsel_cb), NULL); - file_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + file_button = gtk_button_new_with_mnemonic(_("B_rowse")); + gtk_button_set_image((GtkButton*)file_button, + gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); gtk_table_attach(GTK_TABLE(table), file_button, 2, 3, 1, 2, 0, 0, 0, 0); g_signal_connect(G_OBJECT(file_button), "clicked", diff --git a/src/import.c b/src/import.c index ced583827..d055feee1 100644 --- a/src/import.c +++ b/src/import.c @@ -193,14 +193,18 @@ static void import_create(void) gtk_table_attach(GTK_TABLE(table), dest_entry, 1, 2, 1, 2, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); - file_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + file_button = gtk_button_new_with_mnemonic(_("_Browse")); + gtk_button_set_image((GtkButton*)file_button, + gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); gtk_table_attach(GTK_TABLE(table), file_button, 2, 3, 0, 1, 0, 0, 0, 0); g_signal_connect(G_OBJECT(file_button), "clicked", G_CALLBACK(import_filesel_cb), NULL); /* XXX GTK 2.6: GTK_STOCK_DIRECTORY */ - dest_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); + dest_button = gtk_button_new_with_mnemonic(_("B_rowse")); + gtk_button_set_image((GtkButton*)dest_button, + gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON)); gtk_table_attach(GTK_TABLE(table), dest_button, 2, 3, 1, 2, 0, 0, 0, 0); g_signal_connect(G_OBJECT(dest_button), "clicked", diff --git a/src/mbox.c b/src/mbox.c index 79906d087..cf36cf2e7 100644 --- a/src/mbox.c +++ b/src/mbox.c @@ -47,6 +47,7 @@ #define FPUTS_TO_TMP_ABORT_IF_FAIL(s) \ { \ + lines++; \ if (fputs(s, tmp_fp) == EOF) { \ g_warning("can't write to temporary file\n"); \ fclose(tmp_fp); \ @@ -60,10 +61,12 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) { FILE *mbox_fp; - gchar buf[MSGBUFSIZE], from_line[MSGBUFSIZE]; + gchar buf[MSGBUFSIZE]; gchar *tmp_file; gint msgs = 0; + gint lines; MsgInfo *msginfo; + gboolean more; g_return_val_if_fail(dest != NULL, -1); g_return_val_if_fail(mbox != NULL, -1); @@ -91,13 +94,6 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) return -1; } - strcpy(from_line, buf); - if (fgets(buf, sizeof(buf), mbox_fp) == NULL) { - g_warning("malformed mbox: %s\n", mbox); - fclose(mbox_fp); - return -1; - } - tmp_file = get_tmp_file(); folder_item_update_freeze(); @@ -105,8 +101,7 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) do { FILE *tmp_fp; FolderItem *dropfolder; - gint empty_line; - gboolean is_next_msg = FALSE; + gint empty_lines; gint msgnum; if ((tmp_fp = g_fopen(tmp_file, "wb")) == NULL) { @@ -116,86 +111,84 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) g_free(tmp_file); return -1; } - if (change_file_mode_rw(tmp_fp, tmp_file) < 0) + if (change_file_mode_rw(tmp_fp, tmp_file) < 0) { FILE_OP_ERROR(tmp_file, "chmod"); + } - /* convert unix From into Return-Path */ - /* - startp = from_line + 5; - endp = strchr(startp, ' '); - if (endp == NULL) - rpath = g_strdup(startp); - else - rpath = g_strndup(startp, endp - startp); - g_strstrip(rpath); - g_snprintf(from_line, sizeof(from_line), - "Return-Path: %s\n", rpath); - g_free(rpath); - - FPUTS_TO_TMP_ABORT_IF_FAIL(from_line); - */ + empty_lines = 0; + lines = 0; + more = FALSE; - FPUTS_TO_TMP_ABORT_IF_FAIL(buf); - from_line[0] = '\0'; + /* process all lines from mboxrc file */ + while (fgets(buf, sizeof(buf), mbox_fp) != NULL) { + int offset; - empty_line = 0; + /* eof not reached, expect more lines */ + more = TRUE; - while (fgets(buf, sizeof(buf), mbox_fp) != NULL) { + /* eat empty lines */ if (buf[0] == '\n' || buf[0] == '\r') { - empty_line++; - buf[0] = '\0'; + empty_lines++; continue; } - /* From separator */ - while (!strncmp(buf, "From ", 5)) { - strcpy(from_line, buf); - if (fgets(buf, sizeof(buf), mbox_fp) == NULL) { - buf[0] = '\0'; + /* From separator or quoted From */ + offset = 0; + /* detect leading '>' char(s) */ + while ((buf[offset] == '>')) { + offset++; + } + if (!strncmp(buf+offset, "From ", 5)) { + /* From separator: */ + if (offset == 0) { + /* expect next mbox item */ break; } - if (is_header_line(buf)) { - is_next_msg = TRUE; - break; - } else if (!strncmp(buf, "From ", 5)) { - continue; - } else if (!strncmp(buf, ">From ", 6)) { - g_memmove(buf, buf + 1, strlen(buf)); - is_next_msg = TRUE; - break; - } else { - g_warning("unescaped From found:\n%s", - from_line); - break; + /* quoted From: */ + /* flush any eaten empty line */ + if (empty_lines > 0) { + while (empty_lines-- > 0) { + FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); } + empty_lines = 0; + } + /* store the unquoted line */ + FPUTS_TO_TMP_ABORT_IF_FAIL(buf + 1); + continue; } - if (is_next_msg) break; - if (empty_line > 0) { - while (empty_line--) + /* other line */ + /* flush any eaten empty line */ + if (empty_lines > 0) { + while (empty_lines-- > 0) { FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); - empty_line = 0; } - - if (from_line[0] != '\0') { - FPUTS_TO_TMP_ABORT_IF_FAIL(from_line); - from_line[0] = '\0'; + empty_lines = 0; } - - if (buf[0] != '\0') { - if (!strncmp(buf, ">From ", 6)) { - FPUTS_TO_TMP_ABORT_IF_FAIL(buf + 1); - } else + /* store the line itself */ FPUTS_TO_TMP_ABORT_IF_FAIL(buf); + } + /* end of mbox item or end of mbox */ - buf[0] = '\0'; + /* flush any eaten empty line (but the last one) */ + if (empty_lines > 0) { + while (--empty_lines > 0) { + FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); } } - if (empty_line > 0) { - while (--empty_line) - FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); + /* more emails to expect? */ + more = !feof(mbox_fp); + + /* warn if email part is empty (it's the minimum check + we can do */ + if (lines == 0) { + g_warning("malformed mbox: %s: message %d is empty\n", mbox, msgs); + fclose(tmp_fp); + fclose(mbox_fp); + g_unlink(tmp_file); + return -1; } if (fclose(tmp_fp) == EOF) { @@ -222,7 +215,7 @@ gint proc_mbox(FolderItem *dest, const gchar *mbox, gboolean apply_filter) procmsg_msginfo_free(msginfo); msgs++; - } while (from_line[0] != '\0'); + } while (more); folder_item_update_thaw(); @@ -385,9 +378,10 @@ gint export_list_to_mbox(GSList *mlist, const gchar *mbox) _("This file already exists. Do you want to overwrite it?"), _("Overwrite"), GTK_STOCK_CANCEL, NULL, FALSE, NULL, ALERT_WARNING, G_ALERTALTERNATE) - == G_ALERTALTERNATE) + == G_ALERTALTERNATE) { return -1; } + } if ((mbox_fp = g_fopen(mbox, "wb")) == NULL) { FILE_OP_ERROR(mbox, "fopen"); @@ -397,6 +391,7 @@ gint export_list_to_mbox(GSList *mlist, const gchar *mbox) for (cur = mlist; cur != NULL; cur = cur->next) { msginfo = (MsgInfo *)cur->data; + int len; msg_fp = procmsg_open_message(msginfo); if (!msg_fp) { @@ -414,11 +409,32 @@ gint export_list_to_mbox(GSList *mlist, const gchar *mbox) fprintf(mbox_fp, "From %s %s", buf, ctime(&msginfo->date_t)); + buf[0] = '\0'; + + /* write email to mboxrc */ while (fgets(buf, sizeof(buf), msg_fp) != NULL) { - if (!strncmp(buf, "From ", 5)) + /* quote any From, >From, >>From, etc., according to mbox format specs */ + int offset; + + offset = 0; + /* detect leading '>' char(s) */ + while ((buf[offset] == '>')) { + offset++; + } + if (!strncmp(buf+offset, "From ", 5)) fputc('>', mbox_fp); fputs(buf, mbox_fp); } + + /* force last line to end w/ a newline */ + len = strlen(buf); + if (len > 0) { + len--; + if ((buf[len] != '\n') && (buf[len] != '\r')) + fputc('\n', mbox_fp); + } + + /* add a trailing empty line */ fputc('\n', mbox_fp); fclose(msg_fp); diff --git a/src/plugins/trayicon/trayicon.c b/src/plugins/trayicon/trayicon.c index 9d6ea5bbf..ad3b240fe 100644 --- a/src/plugins/trayicon/trayicon.c +++ b/src/plugins/trayicon/trayicon.c @@ -67,6 +67,8 @@ static GtkWidget *eventbox; static GtkWidget *image; static GtkTooltips *tooltips; static GtkWidget *traymenu_popup; +static GtkItemFactory *traymenu_factory; +static gboolean updating_menu = FALSE; guint destroy_signal_id; @@ -84,17 +86,20 @@ static void trayicon_get_all_cb (gpointer data, guint action, GtkWidget *wid static void trayicon_compose_cb (gpointer data, guint action, GtkWidget *widget); static void trayicon_addressbook_cb (gpointer data, guint action, GtkWidget *widget); static void trayicon_exit_cb (gpointer data, guint action, GtkWidget *widget); +static void trayicon_toggle_offline_cb (gpointer data, guint action, GtkWidget *widget); static void resize_cb (GtkWidget *widget, GtkRequisition *req, gpointer user_data); static GtkItemFactoryEntry trayicon_popup_menu_entries[] = { - {N_("/_Get"), NULL, trayicon_get_cb, 0, NULL}, - {N_("/Get _All"), NULL, trayicon_get_all_cb, 0, NULL}, - {N_("/---"), NULL, NULL, 0, ""}, - {N_("/_Email"), NULL, trayicon_compose_cb, 0, NULL}, - {N_("/Open A_ddressbook"), NULL, trayicon_addressbook_cb, 0, NULL}, - {N_("/---"), NULL, NULL, 0, ""}, - {N_("/E_xit Sylpheed"), NULL, trayicon_exit_cb, 0, NULL} + {N_("/_Get"), NULL, trayicon_get_cb, 0, NULL}, + {N_("/Get _All"), NULL, trayicon_get_all_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Email"), NULL, trayicon_compose_cb, 0, NULL}, + {N_("/Open A_ddressbook"), NULL, trayicon_addressbook_cb, 0, NULL}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/_Offline"), NULL, trayicon_toggle_offline_cb, 0, ""}, + {N_("/---"), NULL, NULL, 0, ""}, + {N_("/E_xit Sylpheed"), NULL, trayicon_exit_cb, 0, NULL} }; static void set_trayicon_pixmap(TrayIconType icontype) @@ -126,8 +131,9 @@ static void set_trayicon_pixmap(TrayIconType icontype) break; } - if (pixmap == last_pixmap) + if (pixmap == last_pixmap) { return; + } gtk_image_set_from_pixmap(GTK_IMAGE(image), pixmap, bitmap); @@ -145,15 +151,16 @@ static void update(void) gtk_tooltips_set_tip(tooltips, eventbox, buf, ""); g_free(buf); - - if (new > 0 && unreadmarked > 0) + + if (new > 0 && unreadmarked > 0) { icontype = TRAYICON_NEWMARKED; - else if (new > 0) + } else if (new > 0) { icontype = TRAYICON_NEW; - else if (unreadmarked > 0) + } else if (unreadmarked > 0) { icontype = TRAYICON_UNREADMARKED; - else if (unread > 0) + } else if (unread > 0) { icontype = TRAYICON_UNREAD; + } set_trayicon_pixmap(icontype); } @@ -176,20 +183,29 @@ static gboolean click_cb(GtkWidget * widget, { MainWindow *mainwin; - if (event == NULL) + if (event == NULL) { return TRUE; + } mainwin = mainwindow_get_mainwindow(); - + switch (event->button) { case 1: if (GTK_WIDGET_VISIBLE(GTK_WIDGET(mainwin->window))) { main_window_hide(mainwin); } else { main_window_show(mainwin); - } + } break; case 3: + /* tell callbacks to skip any event */ + updating_menu = TRUE; + /* initialize checkitem according to current offline state */ + gtk_check_menu_item_set_active( + GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item(traymenu_factory, + _("/Offline"))), prefs_common.work_offline); + updating_menu = FALSE; + gtk_menu_popup( GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL, event->button, event->time ); break; @@ -211,49 +227,47 @@ static void destroy_cb(GtkWidget *widget, gpointer *data) static void create_trayicon() { gint n_entries = 0; - GtkItemFactory *traymenu_factory; #if 0 GtkPacker *packer; #endif - trayicon = egg_tray_icon_new("Sylpheed-Claws"); + trayicon = egg_tray_icon_new("Sylpheed-Claws"); gtk_widget_realize(GTK_WIDGET(trayicon)); gtk_window_set_default_size(GTK_WINDOW(trayicon), 16, 16); - gtk_container_set_border_width(GTK_CONTAINER(trayicon), 0); + gtk_container_set_border_width(GTK_CONTAINER(trayicon), 0); - PIXMAP_CREATE(GTK_WIDGET(trayicon), nomail_pixmap, nomail_bitmap, nomail_xpm); - PIXMAP_CREATE(GTK_WIDGET(trayicon), unreadmail_pixmap, unreadmail_bitmap, unreadmail_xpm); - PIXMAP_CREATE(GTK_WIDGET(trayicon), newmail_pixmap, newmail_bitmap, newmail_xpm); - PIXMAP_CREATE(GTK_WIDGET(trayicon), unreadmarkedmail_pixmap, unreadmarkedmail_bitmap, unreadmarkedmail_xpm); - PIXMAP_CREATE(GTK_WIDGET(trayicon), newmarkedmail_pixmap, newmarkedmail_bitmap, newmarkedmail_xpm); + PIXMAP_CREATE(GTK_WIDGET(trayicon), nomail_pixmap, nomail_bitmap, nomail_xpm); + PIXMAP_CREATE(GTK_WIDGET(trayicon), unreadmail_pixmap, unreadmail_bitmap, unreadmail_xpm); + PIXMAP_CREATE(GTK_WIDGET(trayicon), newmail_pixmap, newmail_bitmap, newmail_xpm); + PIXMAP_CREATE(GTK_WIDGET(trayicon), unreadmarkedmail_pixmap, unreadmarkedmail_bitmap, unreadmarkedmail_xpm); + PIXMAP_CREATE(GTK_WIDGET(trayicon), newmarkedmail_pixmap, newmarkedmail_bitmap, newmarkedmail_xpm); - eventbox = gtk_event_box_new(); - gtk_container_set_border_width(GTK_CONTAINER(eventbox), 0); - gtk_container_add(GTK_CONTAINER(trayicon), GTK_WIDGET(eventbox)); + eventbox = gtk_event_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(eventbox), 0); + gtk_container_add(GTK_CONTAINER(trayicon), GTK_WIDGET(eventbox)); - image = gtk_image_new_from_pixmap(nomail_pixmap, nomail_bitmap); - gtk_container_add(GTK_CONTAINER(eventbox), image); + image = gtk_image_new_from_pixmap(nomail_pixmap, nomail_bitmap); + gtk_container_add(GTK_CONTAINER(eventbox), image); destroy_signal_id = g_signal_connect(G_OBJECT(trayicon), "destroy", - G_CALLBACK(destroy_cb), NULL); + G_CALLBACK(destroy_cb), NULL); g_signal_connect(GTK_OBJECT(trayicon), "size-request", - G_CALLBACK(resize_cb), NULL); + G_CALLBACK(resize_cb), NULL); g_signal_connect(G_OBJECT(eventbox), "button-press-event", - G_CALLBACK(click_cb), NULL); + G_CALLBACK(click_cb), NULL); - tooltips = gtk_tooltips_new(); - gtk_tooltips_set_delay(tooltips, 1000); - gtk_tooltips_enable(tooltips); + tooltips = gtk_tooltips_new(); + gtk_tooltips_set_delay(tooltips, 1000); + gtk_tooltips_enable(tooltips); n_entries = sizeof(trayicon_popup_menu_entries) / - sizeof(trayicon_popup_menu_entries[0]); + sizeof(trayicon_popup_menu_entries[0]); traymenu_popup = menu_create_items(trayicon_popup_menu_entries, - n_entries, - "", &traymenu_factory, - NULL); + n_entries, "", &traymenu_factory, + NULL); - gtk_widget_show_all(GTK_WIDGET(trayicon)); + gtk_widget_show_all(GTK_WIDGET(trayicon)); update(); } @@ -278,7 +292,7 @@ int plugin_init(gchar **error) create_trayicon(); - return 0; + return 0; } void plugin_done(void) @@ -288,8 +302,9 @@ void plugin_done(void) gtk_widget_destroy(GTK_WIDGET(trayicon)); hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_id); - while (gtk_events_pending()) - gtk_main_iteration(); + while (gtk_events_pending()) { + gtk_main_iteration(); + } } const gchar *plugin_name(void) @@ -336,12 +351,22 @@ static void trayicon_addressbook_cb( gpointer data, guint action, GtkWidget *wid addressbook_open(NULL); } +static void trayicon_toggle_offline_cb( gpointer data, guint action, GtkWidget *widget ) +{ + /* toggle offline mode if menu checkitem has been clicked */ + if (!updating_menu) { + MainWindow *mainwin = mainwindow_get_mainwindow(); + main_window_toggle_work_offline(mainwin, !prefs_common.work_offline, FALSE); + } +} + static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget) { if (prefs_common.confirm_on_exit) { if (alertpanel(_("Exit"), _("Exit this program?"), - GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) != G_ALERTDEFAULT) + GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL) != G_ALERTDEFAULT) { return; + } manage_window_focus_in(mainwin->window, NULL, NULL); } @@ -352,6 +377,7 @@ static void trayicon_exit_cb( gpointer data, guint action, GtkWidget *widget ) { MainWindow *mainwin = mainwindow_get_mainwindow(); - if (mainwin->lock_count == 0) + if (mainwin->lock_count == 0) { app_exit_cb(mainwin, 0, NULL); + } } -- 2.25.1