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 <unistd.h> 32 #include <getopt.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <locale.h> 36 #include <libgen.h> 37 #include <sys/types.h> 38 #include <sys/wait.h> 39 #include <sys/stat.h> 40 #include <unistd.h> 41 #include <signal.h> 42 43 #include <glib.h> 44 #include <glib/gi18n.h> 45 #include <gconf/gconf-client.h> 46 #include <gnome.h> 47 #include <glade/glade.h> 48 #include <bonobo/bonobo-object.h> 49 #include <bonobo-activation/bonobo-activation.h> 50 51 #include "fsexam-application-server.h" 52 #include "GNOME_Fsexam.h" 53 54 #include "file-filter.h" 55 #include "fsexam.h" 56 #include "fsexam-debug.h" 57 #include "encoding.h" 58 #include "fsexam-log.h" 59 #include "fsexam-dryrun.h" 60 #include "fsexam-history.h" 61 #include "fsexam-pref.h" 62 #include "fsexam-setting.h" 63 #include "file-validate.h" 64 #include "fsexam-ui.h" 65 #include "fsexam-helper.h" 66 #include "fsexam-convname.h" 67 #include "fsexam-convcontent.h" 68 #include "fsexam-specialfile.h" 69 #include "fsexam-encoding-dialog.h" 70 71 gboolean cli_mode = FALSE; /* global var, CLI or GUI */ 72 gboolean force_quit = FALSE; 73 gboolean stop_search = FALSE; 74 75 static FSEXAM_setting *setting = NULL; 76 static GnomeProgram *program = NULL; 77 static BonoboObject *fsexam_app_server = NULL; 78 79 /* Remember command line options */ 80 static gboolean prepend_encoding = FALSE; 81 static gboolean append_encoding = FALSE; 82 static gboolean save_encoding = FALSE; 83 static gboolean restore = FALSE; 84 85 static gchar *special = NULL; 86 static gchar *encoding_list = NULL; 87 static gchar *dryrun_file = NULL; 88 static GList *files = NULL; 89 90 static void signal_handler (int); 91 static void show_usage (void); 92 static void show_version (void); 93 static void decode_options (gint argc, gchar **argv, 94 FSEXAM_pref *pref); 95 static gint CLI_processing (gint argc, gchar **argv); 96 static gint GUI_processing (gint argc, gchar **argv); 97 static gint all_in_one (gint argc, gchar **argv); 98 static gboolean create_config_dir (const gchar *dir); 99 static GList * read_files_from_stdin (GList *files); 100 static FSEXAM_setting * fsexam_init (gint argc, gchar **argv, gboolean cli_mode); 101 102 103 #define OPT_STRING "+abd:E:e:Ff:g:HklL:npPRrSstwV?" 104 static struct option long_options[] = 105 { 106 {"auto-detect", no_argument, NULL, 'a'}, 107 {"batch", no_argument, NULL, 'b'}, 108 {"force-convert", no_argument, NULL, 'F'}, 109 {"hidden", no_argument, NULL, 'H'}, 110 {"no-check-symlink-content",no_argument, NULL, 'k'}, 111 {"list-encoding", no_argument, NULL, 'l'}, 112 {"dry-run", no_argument, NULL, 'n'}, 113 {"append-encoding-list", no_argument, NULL, 'p'}, 114 {"prepend-encoding-list", no_argument, NULL, 'P'}, 115 {"recursive", no_argument, NULL, 'R'}, 116 {"remove", no_argument, NULL, 'r'}, 117 {"save-encoding-list", no_argument, NULL, 'S'}, 118 {"restore", no_argument, NULL, 's'}, 119 {"conv-content", no_argument, NULL, 't'}, 120 {"follow", no_argument, NULL, 'w'}, 121 {"version", no_argument, NULL, 'V'}, 122 {"help", no_argument, NULL, '?'}, 123 {"dry-run-result-file", required_argument, NULL, 'd'}, 124 {"set-history-length", required_argument, NULL, 'g'}, 125 {NULL, 0, NULL, 0}, 126 }; 127 128 129 gint 130 main(gint argc, gchar **argv) 131 { 132 gint ret = FEEXIT_SUCCESS; 133 134 setlocale (LC_ALL, ""); 135 136 bindtextdomain (GETTEXT_PACKAGE, FSEXAM_LOCALEDIR); 137 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); 138 textdomain (GETTEXT_PACKAGE); 139 140 if (strcmp (basename (argv[0]), FSEXAM_CLI_NAME) == 0) 141 cli_mode = TRUE; 142 143 ret = all_in_one (argc, argv); 144 145 return ret; 146 } 147 148 static void 149 signal_handler (int sig) 150 { 151 switch (sig) { 152 case SIGQUIT: 153 case SIGINT: 154 break; 155 156 case SIGALRM: 157 g_print (_("Another fsexam is running, will quit soon\n")); 158 break; 159 } 160 161 fsexam_cleanup_all (); 162 163 exit (EXIT_SUCCESS); 164 } 165 166 void 167 fsexam_cleanup_all () 168 { 169 if (cli_mode) { 170 fsexam_setting_destroy (setting); 171 fsexam_list_free (files); 172 }else if (view != NULL) { 173 if (view->pid > 0) { /* kill subprocess if running */ 174 kill (view->pid, SIGKILL); 175 wait (NULL); 176 } 177 178 fsexam_pref_save_to_gconf (view->setting->pref, 179 view->setting->pref->gconf_client, 180 FALSE); 181 182 fsexam_view_destroy (view); 183 view = NULL; 184 } 185 186 if (G_IS_OBJECT (fsexam_app_server)) 187 bonobo_object_unref (fsexam_app_server); 188 189 return; 190 } 191 192 static void 193 handle_multiple_instance () 194 { 195 CORBA_Environment env; 196 GNOME_Fsexam_Application server; 197 198 alarm (5); 199 200 CORBA_exception_init (&env); 201 202 server = bonobo_activation_activate_from_id ( 203 "OAFIID:GNOME_Fsexam_Application", 204 0, 205 NULL, 206 &env); 207 208 if (server == NULL) { 209 gdk_notify_startup_complete (); 210 return; 211 } 212 213 GNOME_Fsexam_Application_grabFocus (server, &env); 214 g_print (_("Another fsexam is running, will quit soon\n")); 215 216 bonobo_object_release_unref (server, &env); 217 CORBA_exception_free (&env); 218 219 gdk_notify_startup_complete (); 220 221 return; 222 } 223 224 static void 225 decode_options (gint argc, gchar **argv, FSEXAM_pref *pref) 226 { 227 gint optchar; 228 const gchar *find_expr = NULL; 229 230 while ((optchar = getopt_long ( 231 argc, argv, OPT_STRING, long_options, NULL)) != -1){ 232 switch (optchar){ 233 case 'a': 234 pref->auto_detect = TRUE; 235 break; 236 237 case 'b': 238 pref->auto_conversion = TRUE; 239 break; 240 241 case 'd': 242 dryrun_file = optarg; 243 break; 244 245 case 'E': 246 special = optarg; 247 break; 248 249 case 'e': 250 encoding_list = optarg; 251 break; 252 253 case 'F': 254 pref->force = TRUE; 255 break; 256 257 case 'f': 258 find_expr = optarg; 259 break; 260 261 case 'g': 262 pref->hist_len = atoi (optarg); 263 264 case 'H': 265 pref->hidden = TRUE; 266 break; 267 268 case 'k': 269 pref->no_check_symlink_content = TRUE; 270 break; 271 272 case 'l': 273 show_avail_encoding (); /* show supported encoding */ 274 exit (FEEXIT_SUCCESS); 275 276 case 'L': 277 pref->use_log = TRUE; 278 pref->log_file = g_strdup (optarg); 279 break; 280 281 case 'n': 282 pref->dry_run = TRUE; 283 break; 284 285 case 'P': 286 append_encoding = TRUE; 287 break; 288 289 case 'p': 290 prepend_encoding = TRUE; 291 break; 292 293 case 'R': 294 pref->recursive = TRUE; 295 break; 296 297 case 'r': 298 pref->remote = TRUE; 299 break; 300 301 case 'S': 302 save_encoding = TRUE; 303 break; 304 305 case 's': 306 restore = TRUE; 307 break; 308 309 case 't': 310 pref->conv_content = TRUE; 311 break; 312 313 case 'w': 314 pref->follow = TRUE; 315 break; 316 317 case 'V': 318 show_version (); /* show version info and exit */ 319 exit (FEEXIT_SUCCESS); 320 321 case '?': 322 show_usage (); 323 exit (FEEXIT_SUCCESS); 324 325 default: 326 fprintf (stderr, 327 _("Run 'fsexam --help' for usage information.\n")); 328 exit (FEEXIT_FAILURE); 329 } 330 } 331 332 if (pref->hist_len < 30) { 333 g_print (_("Warning: The given history length is too short, will reset to default value %d\n"), HISTORY_LENGTH); 334 pref->hist_len = HISTORY_LENGTH; 335 } 336 337 /* file list */ 338 if (find_expr != NULL) { /* find(1) will suppress other operands */ 339 files = filter_cmd_run (find_expr); 340 }else if (cli_mode) { /* CLI operands */ 341 if (optind < argc){ 342 for (; optind < argc; optind ++){ 343 if ((*argv[optind] == '-') && (*(argv[optind] + 1) == '\0')){ 344 files = read_files_from_stdin (files); 345 break; 346 } 347 348 files = g_list_prepend (files, g_strdup (argv[optind])); 349 } 350 }else{ 351 if ((dryrun_file == NULL) || 352 ((dryrun_file != NULL) && pref->dry_run)) { 353 files = read_files_from_stdin (files); 354 } 355 } 356 }else { /* GUI operands */ 357 if (optind < argc){ 358 for (; optind < argc; optind ++){ 359 files = g_list_append (files, g_strdup (argv[optind])); 360 //files = g_list_prepend (files, g_strdup (argv[optind])); 361 } 362 } 363 } 364 365 return; 366 } 367 368 /* Read file list from stdin */ 369 static GList * 370 read_files_from_stdin (GList *files) 371 { 372 gchar *p = NULL; 373 gchar path[PATH_MAX]; 374 375 fprintf (stdout, _("Please enter the file name:\n")); 376 377 while ((fgets (path, PATH_MAX, stdin)) != NULL){ 378 p = g_strstrip (path); 379 380 if ('\0' != *p){ 381 files = g_list_prepend (files, g_strdup (p)); 382 } 383 } 384 385 return files; 386 } 387 388 static gint 389 CLI_processing (gint argc, gchar **argv) 390 { 391 gint ret = FEEXIT_SUCCESS; 392 CORBA_Object factory; 393 394 program = gnome_program_init ("fsexam", VERSION, 395 LIBGNOMEUI_MODULE, argc, argv, 396 GNOME_PARAM_HUMAN_READABLE_NAME, 397 _("File Encoding Examiner"), 398 GNOME_PROGRAM_STANDARD_PROPERTIES, 399 GNOME_PARAM_APP_DATADIR, DATADIR, 400 NULL); 401 402 if (! bonobo_activation_is_initialized ()) { 403 bonobo_activation_init (argc, argv); 404 } 405 406 factory = bonobo_activation_activate_from_id ( 407 "OAFIID:GNOME_Fsexam_Factory", 408 Bonobo_ACTIVATION_FLAG_EXISTING_ONLY, 409 NULL, NULL); 410 411 if (factory != NULL) { 412 handle_multiple_instance (); 413 return EXIT_SUCCESS; 414 } 415 416 fsexam_app_server = fsexam_application_server_new (NULL); 417 418 if (!setting->pref->dry_run && (dryrun_file != NULL)){ 419 /* Scenario based conversion */ 420 setting->dryrun_info = fsexam_dryrun_file_new (dryrun_file, TRUE); 421 ret = fsexam_convert_scenario (setting); 422 }else if (restore){ 423 /* Restore file name or file content */ 424 if (setting->pref->conv_content) { 425 ret = fsexam_restore (setting, files, RestoreConvContent); 426 }else{ 427 ret = fsexam_restore (setting, files, RestoreConvName); 428 } 429 }else{ 430 /* Convert file name or file content */ 431 if ((NULL == setting->pref->encode_list) && 432 !(setting->pref->auto_detect)) { 433 g_print (_("Error: No given encoding and disabled" 434 " auto detection.\n")); 435 ret = FEEXIT_FAILURE; 436 goto free; 437 } 438 439 if (NULL != dryrun_file) { 440 setting->dryrun_info = fsexam_dryrun_file_new (dryrun_file, FALSE); 441 442 if (setting->dryrun_info == NULL) { 443 fprintf (stderr, _("Can't open dryrun file %s\n"), 444 dryrun_file); 445 ret = FEEXIT_FAILURE; 446 goto free; 447 } 448 449 fsexam_dryrun_write_convtype (setting->dryrun_info, 450 setting->pref->conv_content ? ConvContent : ConvName); 451 } 452 453 if (setting->pref->conv_content) { 454 ret = fsexam_convert_content_batch (setting, files); 455 }else{ 456 ret = fsexam_convert_filename_batch (setting, files); 457 } 458 } 459 460 free: 461 fsexam_cleanup_all (); 462 463 return ret; 464 } 465 466 static gint 467 GUI_processing (gint argc, gchar **argv) 468 { 469 GnomeClient *client = NULL; 470 CORBA_Object factory; 471 472 program = gnome_program_init ("fsexam", VERSION, 473 LIBGNOMEUI_MODULE, argc, argv, 474 GNOME_PARAM_HUMAN_READABLE_NAME, 475 _("File Encoding Examiner"), 476 GNOME_PROGRAM_STANDARD_PROPERTIES, 477 GNOME_PARAM_APP_DATADIR, DATADIR, 478 NULL); 479 480 client = gnome_master_client (); 481 g_signal_connect (G_OBJECT (client), "die", 482 G_CALLBACK (exit), 483 NULL); 484 485 if (! bonobo_activation_is_initialized ()) { 486 bonobo_activation_init (argc, argv); 487 } 488 489 factory = bonobo_activation_activate_from_id ( 490 "OAFIID:GNOME_Fsexam_Factory", 491 Bonobo_ACTIVATION_FLAG_EXISTING_ONLY, 492 NULL, NULL); 493 494 if (factory != NULL) { 495 handle_multiple_instance (); 496 return EXIT_SUCCESS; 497 } 498 499 fsexam_app_server = fsexam_application_server_new ( 500 gdk_screen_get_default ()); 501 502 view = fsexam_view_new (); 503 view->setting = setting; 504 505 /* set the initial state of gui */ 506 fsexam_gui_set_initial_state (); 507 508 /* set dryrun buffer */ 509 fsexam_dryrun_buffer_set_buffer ( 510 FSEXAM_DRYRUN_BUFFER (setting->dryrun_info), 511 gtk_text_view_get_buffer (GTK_TEXT_VIEW ( 512 g_object_get_data (G_OBJECT (view->mainwin), 513 "textview_dryrun")))); 514 515 516 if (files == NULL) { 517 fsexam_construct_ui ("."); 518 }else if (g_list_length (files) == 1) { 519 fsexam_construct_ui (files->data); 520 }else{ 521 fsexam_construct_ui ("."); 522 fsexam_search_treeview_append_list (files); 523 } 524 525 fsexam_list_free (files); 526 527 gtk_main (); 528 529 return FEEXIT_SUCCESS; 530 } 531 532 /* 533 * Create "$HOME/.fsexam" 534 */ 535 static gboolean 536 create_config_dir (const gchar *dir) 537 { 538 struct stat statbuf; 539 540 if (NULL == dir) { 541 g_print (_("The configuration directory is NULL\n")); 542 return FALSE; 543 } 544 545 if (stat (dir, &statbuf) == -1) { 546 gint status = mkdir (dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 547 if (status == -1) { 548 g_print (_("Can't create configuration directory %s for fsexam\n"), dir); 549 return FALSE; 550 }else{ 551 return TRUE; 552 } 553 } 554 555 if (S_ISDIR (statbuf.st_mode)) { 556 return TRUE; 557 } 558 559 g_print (_("file %s exist and is not one directory, please rename it.\n"), dir); 560 561 return FALSE; 562 } 563 564 static FSEXAM_setting * 565 fsexam_init (gint argc, gchar **argv, gboolean cli_mode) 566 { 567 FSEXAM_setting *setting = NULL; 568 gchar *fsexam_repo = NULL; 569 gchar *hist_file = NULL; 570 571 fsexam_repo = g_build_filename (g_get_home_dir (), FSEXAM_HIDDEN, NULL); 572 573 if (! create_config_dir (fsexam_repo)) { 574 g_free (fsexam_repo); 575 return NULL; 576 } 577 578 hist_file = g_build_filename (fsexam_repo, HISTORY_FILE, NULL); 579 580 /* Init FSEXAM_setting */ 581 setting = fsexam_setting_init (cli_mode); 582 583 decode_options (argc, argv, setting->pref); 584 585 fsexam_pref_set_encoding_list(setting->pref, 586 encoding_list, 587 append_encoding, 588 prepend_encoding, 589 save_encoding); 590 591 /* Special file handling */ 592 if ((special != NULL) 593 && ((strcmp (special, "COMPRESS") == 0) 594 || strcmp (special, "ALL") == 0)) { 595 setting->pref->special = SPECIAL_COMPRESS; 596 } 597 598 /* log */ 599 if (setting->pref->use_log) { 600 if (setting->log_info != NULL) { 601 fsexam_log_close (setting->log_info); 602 } 603 604 setting->log_info = fsexam_log_open (setting->pref->log_file); 605 /* 606 * Need use abs path for log_file, otherwise will lost path informaiton 607 * after saved into gconf database 608 */ 609 if (*setting->pref->log_file != '/') { 610 char *tmp = get_abs_path (setting->pref->log_file); 611 g_free (setting->pref->log_file); 612 setting->pref->log_file = tmp; 613 } 614 } 615 616 /* initiate remote path list */ 617 setting->remote_path = get_remote_paths (); 618 setting->hist_info = fsexam_history_open (hist_file, 619 setting->pref->hist_len); 620 621 /* Init function pointer */ 622 if (cli_mode) { 623 setting->get_index = get_index_default; 624 setting->update_gui = NULL; 625 setting->display_msg = display_msg_default; 626 setting->display_stats = fsexam_setting_display_stats; 627 }else{ 628 setting->get_index = fsexam_gui_get_index; 629 setting->update_gui = fsexam_gui_update; 630 setting->display_msg = fsexam_gui_display_msg; 631 setting->display_stats = fsexam_gui_display_stats; 632 } 633 634 if (fsexam_debug () & FSEXAM_DBG_OPTION){ 635 fsexam_setting_print (setting); 636 g_list_foreach (files, list_print, "\t"); 637 } 638 if (fsexam_debug () & FSEXAM_DBG_ENCODING) { 639 g_list_foreach (setting->pref->encode_list, print_encoding, NULL); 640 } 641 642 g_free (hist_file); 643 g_free (fsexam_repo); 644 645 return setting; 646 } 647 648 static gint 649 all_in_one (gint argc, gchar *argv[]) 650 { 651 gint ret; 652 653 if ((signal (SIGINT, signal_handler) == SIG_ERR) 654 || (signal (SIGQUIT, signal_handler) == SIG_ERR) 655 || (signal (SIGALRM, signal_handler) == SIG_ERR)) { 656 perror ("Can't set signal handler."); 657 return EXIT_FAILURE; 658 } 659 660 g_type_init (); 661 setting = fsexam_init (argc, argv, cli_mode); 662 if (NULL == setting) { 663 return EXIT_FAILURE; 664 } 665 666 if (cli_mode) 667 ret = CLI_processing (argc, argv); 668 else 669 ret = GUI_processing (argc, argv); 670 671 return ret; 672 } 673 674 static void 675 show_usage () 676 { 677 printf (_("Usage:\n")); 678 printf (_(" fsexam [OPTION] ... [file]\n")); 679 680 printf (_("\nSupported options:\n")); 681 682 printf (_(" -a, --auto-detect Enable encoding auto detection\n")); 683 if (cli_mode) 684 printf (_(" -d dry-run-result Specify the dryrun result file\n")); 685 printf (_(" -E module-name Enable special file handling\n")); 686 printf (_(" -e encoding-list Specify additional encoding list\n")); 687 printf (_(" -F, --force-convert Forceful conversion mode\n")); 688 printf (_(" -f 'expression' Specify file filter criteria\n")); 689 if (cli_mode) 690 printf (_(" -g history-length Set the history length.\n")); 691 printf (_(" -H, --hidden Turn on hidden file handling\n")); 692 printf (_(" -b, --batch Non-interactive mode\n")); 693 printf (_(" -l, list-encoding List all supported encoding\n")); 694 printf (_(" -k, --no-check-symlink-content\n")); 695 printf (_(" Don't check the consistency between\n" 696 " symbolic link and its target name\n")); 697 printf (_(" -L logfile Specify log file\n")); 698 if (cli_mode) 699 printf (_(" -n, --dry-run Dryrun mode\n")); 700 printf (_(" -P, --append-encoding-list\n")); 701 printf (_(" Append encoding list specified by\n" 702 " '-e' to predefined encoding list\n")); 703 printf (_(" -p, --prepend-encoding-list\n")); 704 printf (_(" Prepend encoding list specified by\n" 705 " '-e' to predefined encoding list\n")); 706 printf (_(" -R, --recursive Recursive mode\n")); 707 printf (_(" -r, --remote Turn on nfs files handling\n")); 708 printf (_(" -S, --save-encoding-list\n")); 709 printf (_(" Save encoding list specified by\n" 710 " '-e' permanently\n")); 711 printf (_(" -s, --restore Restore the original name or\n" 712 " content for given files\n")); 713 printf (_(" -t, --conv-content Convert file content rather than\n" 714 " file name\n")); 715 printf (_(" -w, --follow Follow symbolic link\n")); 716 printf (_(" -V, --version Print the version information\n")); 717 printf (_(" -?, --help Print this usage information for fsexam\n")); 718 719 return; 720 } 721 722 static void 723 show_version () 724 { 725 printf ("%s %s\n", 726 cli_mode ? FSEXAM_CLI_NAME : FSEXAM_GUI_NAME, 727 VERSION); 728 729 return; 730 } 731