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 <string.h> 32 #include <glib.h> 33 #include <stdio.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 #include <fcntl.h> 37 38 #include <glib/gi18n.h> 39 #include <gtk/gtk.h> 40 41 #include "fsexam-debug.h" 42 #include "fsexam-header.h" 43 44 void cb_text_buffer_changed (GtkTextBuffer *buffer, gpointer data); 45 46 static void fsexam_dryrun_base_init (gpointer g_class); 47 48 static void dryrun_file_instance_init (GTypeInstance *instance, 49 gpointer g_class); 50 static void dryrun_file_interface_init (gpointer g_iface, 51 gpointer iface_data); 52 static void dryrun_file_class_init (GObjectClass *klass); 53 54 static void dryrun_buffer_instance_init (GTypeInstance *instance, 55 gpointer g_class); 56 static void dryrun_buffer_interface_init (gpointer g_iface, 57 gpointer iface_data); 58 static void dryrun_buffer_class_init (GObjectClass *klass); 59 60 static gboolean fsexam_dryrun_write_path (FsexamDryrun *, const gchar *); 61 62 static gboolean fsexam_dryrun_write_candidate (FsexamDryrun *info, 63 const gchar *encoding_name, 64 const gchar *sample); 65 66 /* FsexamDryrun Interface Implementation */ 67 static void 68 fsexam_dryrun_base_init (gpointer g_class) 69 { 70 static gboolean initialized = FALSE; 71 72 if (!initialized) { 73 initialized = TRUE; 74 } 75 76 return; 77 } 78 79 GType 80 fsexam_dryrun_get_type (void) 81 { 82 static GType type = 0; 83 84 if (type == 0) { 85 static const GTypeInfo info = { 86 sizeof (FsexamDryrunInterface), 87 fsexam_dryrun_base_init, /* base_init */ 88 NULL, /* bsae_finalize */ 89 NULL, /* class_init */ 90 NULL, /* class_finalize */ 91 NULL, /* class_data */ 92 0, /* sizeof (Instance) */ 93 0, /* n_preallocs */ 94 NULL, /* instance_init */ 95 }; 96 97 type = g_type_register_static (G_TYPE_INTERFACE, 98 "FsexamDryrun", 99 &info, 100 0); 101 } 102 103 return type; 104 } 105 106 gboolean 107 fsexam_dryrun_write (FsexamDryrun *self, const gchar *string) 108 { 109 return FSEXAM_DRYRUN_GET_INTERFACE (self)->write (self, string); 110 } 111 112 gboolean 113 fsexam_dryrun_read (FsexamDryrun *self, gchar *buf, guint size) 114 { 115 return FSEXAM_DRYRUN_GET_INTERFACE (self)->read (self, buf, size); 116 } 117 118 /* --- Dryrun implementation on file --- */ 119 static void 120 dryrun_file_finalize (GObject *object) 121 { 122 FsexamDryrunFile *dryrun_file = FSEXAM_DRYRUN_FILE (object); 123 124 if (dryrun_file && dryrun_file->fp) { 125 fclose (dryrun_file->fp); 126 dryrun_file->fp = NULL; 127 } 128 129 return; 130 } 131 132 GType 133 fsexam_dryrun_file_get_type (void) 134 { 135 static GType type = 0; 136 137 if (type == 0) { 138 static const GTypeInfo info = { 139 sizeof (FsexamDryrunFileClass), 140 NULL, /* base_init */ 141 NULL, /* base_finalize */ 142 (GClassInitFunc) dryrun_file_class_init, 143 NULL, /* class_finalize */ 144 NULL, /* class_data */ 145 sizeof (FsexamDryrunFile), 146 0, /* n_preallocs */ 147 (GInstanceInitFunc) dryrun_file_instance_init, 148 }; 149 150 static const GInterfaceInfo dryrun_info = { 151 (GInterfaceInitFunc) dryrun_file_interface_init,/* interface_init */ 152 NULL, /* interface_finalize */ 153 NULL, /* interface_data */ 154 }; 155 156 type = g_type_register_static (G_TYPE_OBJECT, "FsexamDryrunFile", &info, 0); 157 g_type_add_interface_static (type, FSEXAM_TYPE_DRYRUN, &dryrun_info); 158 } 159 160 return type; 161 } 162 163 /* write callback implementation of DryrunFile */ 164 static gboolean 165 dryrun_file_write (FsexamDryrunFile *self, const gchar *string) 166 { 167 gboolean ret = TRUE; 168 169 if (fputs (string, self->fp) < 0) 170 ret = FALSE; 171 172 return ret; 173 } 174 175 static gboolean 176 dryrun_file_read (FsexamDryrunFile *self, gchar *buf, guint size) 177 { 178 gboolean ret = TRUE; 179 180 if (fgets (buf, size, self->fp) == NULL) 181 ret = FALSE; 182 183 return ret; 184 } 185 186 static void 187 dryrun_file_interface_init (gpointer g_iface, gpointer iface_data) 188 { 189 FsexamDryrunInterface *iface = (FsexamDryrunInterface *)g_iface; 190 191 iface->write = (gboolean (*) (FsexamDryrun *, const gchar *))dryrun_file_write; 192 iface->read = (gboolean (*) (FsexamDryrun *, gchar *, guint)) dryrun_file_read; 193 194 return; 195 } 196 197 static void 198 dryrun_file_instance_init (GTypeInstance *instance, gpointer g_class) 199 { 200 FsexamDryrunFile *self = FSEXAM_DRYRUN_FILE (instance); 201 202 self->fp = NULL; 203 204 return; 205 } 206 207 static void 208 dryrun_file_class_init (GObjectClass *klass) 209 { 210 klass->finalize = dryrun_file_finalize; 211 212 return; 213 } 214 215 FsexamDryrun * 216 fsexam_dryrun_file_new (const gchar *filename, gboolean readonly) 217 { 218 FsexamDryrunFile *dryrun = NULL; 219 220 if (filename == NULL) 221 return NULL; 222 223 dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_FILE, NULL); 224 dryrun->fp = fopen (filename, readonly ? "r" : "w"); 225 226 if (dryrun->fp == NULL) { 227 g_object_unref (dryrun); 228 return NULL; 229 } 230 231 return FSEXAM_DRYRUN (dryrun); 232 } 233 234 /* --- Widget implementation for dryrun --- */ 235 GType 236 fsexam_dryrun_buffer_get_type (void) 237 { 238 static GType type = 0; 239 240 if (type == 0) { 241 static const GTypeInfo info = { 242 sizeof (FsexamDryrunBufferClass), 243 NULL, /* base_init */ 244 NULL, /* base_finalize */ 245 (GClassInitFunc) dryrun_buffer_class_init, 246 NULL, /* class_finalize */ 247 NULL, /* class_data */ 248 sizeof (FsexamDryrunBuffer), 249 0, /* n_preallocs */ 250 (GInstanceInitFunc) dryrun_buffer_instance_init, 251 }; 252 253 static const GInterfaceInfo dryrun_info = { 254 (GInterfaceInitFunc) dryrun_buffer_interface_init, 255 NULL, /* interface_finalize */ 256 NULL, /* interface_data */ 257 }; 258 259 type = g_type_register_static (G_TYPE_OBJECT, "FsexamDryrunBuffer", &info, 0); 260 g_type_add_interface_static (type, FSEXAM_TYPE_DRYRUN, &dryrun_info); 261 } 262 263 return type; 264 } 265 266 /* 267 * Create mark and connect signal handler for GtkTextBuffer 268 */ 269 static void 270 setup_text_buffer (GtkTextBuffer *buffer) 271 { 272 GtkTextIter iter; 273 274 if (buffer == NULL) 275 return; 276 277 gtk_text_buffer_get_end_iter (buffer, &iter); 278 gtk_text_buffer_create_mark (buffer, "end_mark", &iter, FALSE); 279 280 g_signal_connect (G_OBJECT (buffer), "changed", 281 G_CALLBACK (cb_text_buffer_changed), NULL); 282 283 return; 284 } 285 286 static gboolean 287 dryrun_buffer_write (FsexamDryrunBuffer *self, const gchar *string) 288 { 289 GtkTextIter iter; 290 291 if (self->buffer == NULL) 292 return FALSE; 293 294 gtk_text_buffer_get_end_iter (self->buffer, &iter); 295 gtk_text_buffer_insert (self->buffer, &iter, string, -1); 296 297 return TRUE; 298 } 299 300 /* 301 * read one line from text buffer and increase line number 302 */ 303 static gboolean 304 dryrun_buffer_read (FsexamDryrunBuffer *self, gchar *buf, guint size) 305 { 306 gboolean ret = TRUE; 307 guint line_count; 308 GtkTextIter start, end; 309 gchar *text = NULL; 310 311 line_count = (guint) gtk_text_buffer_get_line_count (self->buffer); 312 line_count --; /* The last line is empty line */ 313 314 if (self->current_line >= line_count) /* EOF */ 315 return FALSE; 316 317 gtk_text_buffer_get_iter_at_line (self->buffer, 318 &start, 319 self->current_line); 320 gtk_text_buffer_get_iter_at_line (self->buffer, 321 &end, 322 self->current_line + 1); 323 324 text = gtk_text_buffer_get_text (self->buffer, &start, &end, FALSE); 325 326 strlcpy (buf, text, size); 327 328 g_free (text); 329 self->current_line++; 330 331 return ret; 332 } 333 334 static void 335 dryrun_buffer_interface_init (gpointer g_iface, gpointer iface_data) 336 { 337 FsexamDryrunInterface *iface = (FsexamDryrunInterface *)g_iface; 338 339 iface->write = (gboolean (*) (FsexamDryrun *, const gchar *))dryrun_buffer_write; 340 iface->read = (gboolean (*) (FsexamDryrun *, gchar *, guint)) dryrun_buffer_read; 341 342 return; 343 } 344 345 static void 346 dryrun_buffer_instance_init (GTypeInstance *instance, gpointer g_class) 347 { 348 FsexamDryrunBuffer *self = FSEXAM_DRYRUN_BUFFER (instance); 349 350 self->buffer = NULL; 351 self->current_line = 0; 352 353 return; 354 } 355 356 static void 357 dryrun_buffer_finalize (GObject *object) 358 { 359 return; 360 } 361 362 static void 363 dryrun_buffer_class_init (GObjectClass *klass) 364 { 365 klass->finalize = dryrun_buffer_finalize; 366 367 return; 368 } 369 370 FsexamDryrun * 371 fsexam_dryrun_buffer_new (void) 372 { 373 FsexamDryrunBuffer *dryrun = NULL; 374 375 dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_BUFFER, NULL); 376 377 return FSEXAM_DRYRUN (dryrun); 378 } 379 380 FsexamDryrun * 381 fsexam_dryrun_buffer_new_with_buffer (GtkTextBuffer *buffer) 382 { 383 FsexamDryrunBuffer *dryrun = NULL; 384 385 dryrun = g_object_new (FSEXAM_TYPE_DRYRUN_BUFFER, NULL); 386 387 if (buffer != NULL) { 388 dryrun->buffer = buffer; 389 setup_text_buffer (buffer); 390 } 391 392 return FSEXAM_DRYRUN (dryrun); 393 } 394 395 void 396 fsexam_dryrun_buffer_clear_buffer (FsexamDryrunBuffer *buffer) 397 { 398 GtkTextIter start, end; 399 400 gtk_text_buffer_get_start_iter (buffer->buffer, &start); 401 gtk_text_buffer_get_end_iter (buffer->buffer, &end); 402 gtk_text_buffer_delete (buffer->buffer, &start, &end); 403 404 buffer->current_line = 0; 405 406 return; 407 } 408 409 void 410 fsexam_dryrun_buffer_set_buffer (FsexamDryrunBuffer *buffer, 411 GtkTextBuffer *buf) 412 { 413 if (buffer == NULL) 414 return; 415 416 buffer->buffer = buf; 417 setup_text_buffer (buffer->buffer); 418 419 return; 420 } 421 422 void 423 fsexam_dryrun_buffer_set_current_line (FsexamDryrunBuffer *buffer, guint line_no) 424 { 425 if (buffer == NULL) 426 return; 427 428 buffer->current_line = line_no; 429 430 return; 431 } 432 433 /* 434 * Write the full file path into dryrun file 435 */ 436 static gboolean 437 fsexam_dryrun_write_path (FsexamDryrun *info, const gchar *path) 438 { 439 if ((NULL == path) || (NULL == info)) 440 return FALSE; 441 442 if (g_utf8_validate (path, -1, NULL)){ 443 if (! fsexam_dryrun_write (info, path)){ 444 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 445 goto fail; 446 } 447 }else{ 448 gchar *uri = NULL; 449 uri = g_filename_to_uri (path, NULL, NULL); 450 if (NULL == uri){ 451 fsexam_errno = ERR_CANNOT_CONVERT_TO_URI; 452 goto fail; 453 } 454 if (! fsexam_dryrun_write (info, uri) < 0){ 455 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 456 g_free (uri); 457 goto fail; 458 } 459 g_free (uri); 460 } 461 462 fsexam_dryrun_write (info, "\n"); 463 464 return TRUE; 465 466 fail: 467 return FALSE; 468 } 469 470 static gboolean 471 fsexam_dryrun_write_candidate (FsexamDryrun *info, 472 const gchar *encoding_name, 473 const gchar *sample) 474 { 475 if ((NULL == info) || (NULL == sample) || (NULL == encoding_name)) 476 return FALSE; 477 478 if (! fsexam_dryrun_write (info, "\t")) { 479 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 480 return FALSE; 481 } 482 483 fsexam_dryrun_write (info, encoding_name); 484 fsexam_dryrun_write (info, "\t"); 485 fsexam_dryrun_write (info, sample); 486 fsexam_dryrun_write (info, "\n"); 487 488 return TRUE; 489 } 490 491 gboolean 492 fsexam_dryrun_write_msg (FsexamDryrun *info, const gchar *msg) 493 { 494 if ((NULL == info) || (NULL == msg)) 495 return FALSE; 496 497 if (! fsexam_dryrun_write (info, "\t")) { 498 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 499 return FALSE; 500 } 501 502 fsexam_dryrun_write (info, msg); 503 fsexam_dryrun_write (info, "\n"); 504 505 return TRUE; 506 } 507 508 gboolean 509 fsexam_dryrun_write_convtype (FsexamDryrun *info, ConvType type) 510 { 511 g_return_val_if_fail ((info != NULL), FALSE); 512 513 if (type == ConvName) 514 fsexam_dryrun_write (info, "Name conversion\n"); 515 else 516 fsexam_dryrun_write (info, "Content conversion\n"); 517 518 return TRUE; 519 } 520 521 /* 522 * Get the conversion type, and set fsexam_errno if invalid 523 */ 524 gboolean 525 fsexam_dryrun_get_convtype (FsexamDryrun *info, ConvType *type) 526 { 527 const gint TYPE_LENGTH = 80; 528 gchar convtype[TYPE_LENGTH]; 529 gchar *strip = NULL; 530 531 if (! fsexam_dryrun_read (info, convtype, sizeof (convtype))){ 532 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 533 return FALSE; 534 } 535 536 strip = g_strstrip (convtype); 537 538 if (strcmp (strip, "Name conversion") == 0) 539 *type = ConvName; 540 else if (strcmp (strip, "Content conversion") == 0) 541 *type = ConvContent; 542 else 543 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 544 545 return TRUE; 546 } 547 548 /* 549 * Analyze dryrun result file, add Dryrun_item which represent 550 * user selected encoding into GSList result. 551 */ 552 gboolean 553 fsexam_dryrun_process (FsexamDryrun *info, GSList **result) 554 { 555 g_return_val_if_fail (info != NULL, FALSE); 556 557 gchar linebuf [PATH_MAX + 20]; 558 gchar *path = NULL, *encoding = NULL, *sample = NULL, *ptr = NULL; 559 GSList *slist = NULL; 560 gboolean metpath = FALSE, metcode = FALSE, tab = FALSE; 561 gboolean ret = FALSE; 562 563 while (fsexam_dryrun_read (info, linebuf, sizeof (linebuf))) { 564 if (linebuf[strlen (linebuf) - 1] == '\n') 565 linebuf[strlen (linebuf) - 1] = '\0'; 566 567 /* remove leading and trailing white space */ 568 if ((ptr = str_compress (linebuf, &tab)) == NULL) { 569 continue; /* Empty line */ 570 } 571 572 /* 573 * Some files may not need convert at all, or have no 574 * proper encoding. bypass it and continue. 575 */ 576 if ((strcmp (ptr, FSEXAM_DRYRUN_NO_NEED_CONVERT) == 0) 577 || (strcmp (ptr, FSEXAM_DRYRUN_NO_PROPER_ENCODING) == 0)) { 578 if (!metpath) { 579 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 580 goto done; 581 } 582 583 g_free (path); 584 path = NULL; 585 metpath = FALSE; 586 continue; 587 } 588 589 if (tab) { 590 Dryrun_item *item = NULL; 591 592 if (!metpath) { 593 if (metcode) { /* metpath will be false after first candidate */ 594 continue; /* ignore non-first encoding candidate */ 595 }else{ 596 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 597 goto done; 598 } 599 } 600 601 metcode = TRUE; 602 603 str_split (ptr, &encoding, &sample); 604 if ((encoding2id (encoding) == -1) || (sample == NULL)) { 605 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 606 goto done; 607 } 608 609 /* 610 * Found one valid path/encoding pair. 611 */ 612 if ((item = g_new0 (Dryrun_item, 1)) == NULL) { 613 fsexam_errno = ERR_NO_MEMORY; 614 goto done; 615 } 616 617 item->path = path; 618 item->encoding = encoding; 619 620 slist = g_slist_prepend (slist, item); 621 622 g_free (sample); 623 path = NULL; 624 encoding = NULL; 625 sample = NULL; 626 627 metpath = FALSE; 628 }else{ 629 if (metpath) { 630 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 631 goto done; 632 } 633 634 if ('/' == *ptr) { 635 path = g_strdup (ptr); 636 }else{ 637 path = g_filename_from_uri (ptr, NULL, NULL); 638 } 639 640 if (NULL == path) { 641 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 642 goto done; 643 } 644 645 metpath = TRUE; 646 metcode = FALSE; 647 } 648 } 649 650 if (NULL == slist) 651 fsexam_errno = ERR_DRYRUN_FILE_INVALID; 652 else 653 slist = g_slist_reverse (slist); 654 655 *result = slist; /* caller must pass in this param */ 656 ret = TRUE; 657 658 done: 659 g_free (path); 660 g_free (encoding); 661 g_free (sample); 662 663 return ret; 664 } 665 666 void 667 fsexam_dryrun_item_slist_free (GSList *slist) 668 { 669 if (NULL == slist) 670 return; 671 672 while (slist != NULL) { 673 Dryrun_item *item = slist->data; 674 675 g_free (item->path); 676 g_free (item->encoding); 677 g_free (item); 678 679 slist = slist->next; 680 } 681 682 g_slist_free (slist); 683 684 return; 685 } 686 687 /* 688 * Write one dryrun item into file 689 */ 690 gboolean 691 fsexam_dryrun_puts (FsexamDryrun *info, 692 const gchar *fullpath, 693 Score score, /* total score */ 694 GList *encoding_list, 695 ConvType convtype) 696 { 697 gboolean ret = TRUE; 698 699 if ((NULL == info) || (NULL == fullpath) || (NULL == encoding_list)) 700 return FALSE; 701 702 if (! fsexam_dryrun_write_path (info, fullpath)) { 703 return FALSE; 704 } 705 706 if (score == FAIL){ 707 if (! fsexam_dryrun_write_msg (info, 708 _(FSEXAM_DRYRUN_NO_PROPER_ENCODING))){ 709 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 710 ret = FALSE; 711 } 712 }else if (score == ORIGINAL){ 713 if (! fsexam_dryrun_write_msg (info, 714 _(FSEXAM_DRYRUN_NO_NEED_CONVERT))) { 715 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 716 ret = FALSE; 717 } 718 }else{ 719 while (encoding_list) { 720 Encoding *encoding = NULL; 721 gchar *sample = NULL; 722 723 encoding = (Encoding *)encoding_list->data; 724 encoding_list = g_list_next (encoding_list); 725 726 if ((encoding->score == FAIL) || (encoding->score == ORIGINAL)){ 727 continue; 728 } 729 730 if (convtype == ConvName) { 731 sample = encoding->u.converted_text; 732 }else{ 733 sample = encoding->u.contents; 734 } 735 736 if (! fsexam_dryrun_write_candidate (info, 737 id2encoding (encoding->encodingID), 738 sample)) { 739 fsexam_errno = ERR_CANNOT_WRITE_DRYRUN; 740 ret = FALSE; 741 } 742 /* BIG bug: double free 743 if (convtype != ConvName) 744 g_free (sample); 745 */ 746 } 747 } 748 749 return ret; 750 } 751