On Saturday 11 June 2005 11:32, Dinh Duong Ha wrote:
>
> Ky~ thua^.t ma` /me du`ng la` cha(.n signal SGN_CHILD, trong ha`m xu+?
> li' signal na`y, go.i ha`m wait dde^? cho+` child process exit.
> The^m: Ne^'u parent process ke^'t thu'c tru+o+'c child process thi`
> child process se~ co' parent process la` 1- init process, va` init
> process se~ gia?i pho'ng tho^ng tin ve^` child process khi child process
> ke^'t thu'c => gia?i pho'ng process id cho tie^'n tri`nh kha'c.
>

Xin cha`o ba'c. Ca'm o+n ba'c dda~ go+.i y'. Em dda~ la`m ddu+o+.c... su+? 
du.ng signal SGN_CHILD. Tuy nhie^n, va^~n sinh ra tru.c tra(.c kha'c: ddo' 
la` ddoi khi child_process thoa't , nhu+ng winefish ko^ the^? gia?i pho'ng 
resource du+o+.c.

* tru+o+'c khi cha.y child_process: resource chie^'m bo+?i winefish la` 0%
* khi cha.y child_process: winefish chie^'m 0%, child chie^'m khoa?ng 30% ta`i 
nguyene
* sau khi child_process ke^'t thu'c (kho^ng bie^'t ke^' thu'c kie^?u gi`), 
winefish chie^'m 30% ta`i nguye^n <=== lo^~i.

Y' cu?a em la`: ta`i nguye^n cu?a tie^'n tri`nh con ddu+o+.c gia?i pho'ng 
the^' na`o, trong hai tru+o+`ng ho+.p:
* tie^'n tri`nh con thoa't bi`nh thu+o+`ng?
* tie^'n tri`nh con bi. gu+?i signal SIGTERM?

Xem ta^.p tin ddi'nh ke`m.

Ca'm o+n ba'c ra^'t nhie^`u.

-- 
kyanh [ http://kyanh.dotgeek.org/ ]
/* $Id: outputbox.c,v 1.3 2005/07/04 03:30:56 kyanh Exp $ */

/* Winefish LaTeX Editor (based on Bluefish HTML Editor)
 * outputbox.c the output box
 *
 * Copyright (C) 2002 Olivier Sessink
 * Modified for Winefish (C) 2005 Ky Anh <[EMAIL PROTECTED]> 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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
 */ 
/* #define DEBUG */

#include <gtk/gtk.h>
#include <sys/types.h>
#include <signal.h> /* kill() */
#include <sys/stat.h> /* open() */
#include <fcntl.h> /* kyanh, open() */ 
/* kyanh, 20050301,
Thanks to M. Garoche <[EMAIL PROTECTED]>
Move from <wait.h> to <sys/wait.h>
*/
#include <sys/wait.h> /* wait(), waitid() */
#include <regex.h>
#include <stdlib.h>
#include <string.h> /* strlen() */

/* Using waitpid() to free child process's resources,
we don't need anymore <signal.h>. Anyway, thanks to
M. Garoche for reporting a problem with Mac OS */

#include "bluefish.h"
#include "outputbox.h" /* myself */
#include "bf_lib.h"
#include "document.h"
#include "gtk_easy.h"
#include "stringlist.h"
#include "pixmap.h"

static void ob_lview_row_activated_lcb( GtkTreeView *tree, GtkTreePath *path, GtkTreeViewColumn *column, Toutputbox *ob )
{
	GtkTreeIter iter;
	gchar *file, *line;
	gint lineval;
	gtk_tree_model_get_iter( GTK_TREE_MODEL( ob->lstore ), &iter, path );
	gtk_tree_model_get( GTK_TREE_MODEL( ob->lstore ), &iter, 0, &file, 1, &line, -1 );
	g_print( "ob_lview_row_activated_lcb, file=%s, line=%s\n", file, line );
	if ( file && strlen( file ) ) {
		doc_new_with_file( ob->bfwin, file, FALSE, FALSE );
	}
	if ( line && strlen( line ) ) {
		lineval = atoi( line );
		flush_queue();
		doc_select_line( ob->bfwin->current_document, lineval, TRUE );
	}
	g_free( line );
	g_free( file );
}

static void outputbox_close_clicked_lcb( GtkWidget *widget, Toutputbox *ob )
{
	setup_toggle_item_from_widget( ob->bfwin->menubar, N_( "/View/View Outputbox" ), FALSE );
	gtk_widget_hide( ob->hbox );
}

static sig_atomic_t child_exit_status;

static void clean_up_child_process (gint signal_number)
{
	/* Clean up the child process. */
	gint status;
	wait (&status);
	/* Store its exit status in a global variable. */
	child_exit_status = status;
}

/*
static GtkWidget *create_popup_menu ()
{
	GtkWidget *menu;
	GtkWidget *menu_item;

	menu = gtk_menu_new ();

	/ * Add the separator * /
	menu_item = gtk_separator_menu_item_new ();
	gtk_widget_show (menu_item);
	gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
	
	return menu;
}
	*/

static Toutputbox *init_outputbox( Tbfwin *bfwin )
{
	/* we call this once */
	/* Handle SIGCHLD by calling clean_up_child_process. */
	struct sigaction sigchld_action;
	memset (&sigchld_action, 0, sizeof (sigchld_action));
	sigchld_action.sa_handler = &clean_up_child_process;
	sigaction (SIGCHLD, &sigchld_action, NULL);

	/* create the backend */

	GtkTreeViewColumn * column;
	GtkWidget *scrolwin;
	GtkCellRenderer *renderer;
	GtkWidget *vbox2, *but, *image;
	Toutputbox *ob;

	ob = g_new0( Toutputbox, 1 );
	DEBUG_MSG( "init_output_box, created %p\n", ob );
	bfwin->outputbox = ob;
	ob->bfwin = bfwin;
	ob->def = NULL;
	ob->pid=0;

	ob->hbox = gtk_hbox_new( FALSE, 0 );
	gtk_paned_add2( GTK_PANED( bfwin->vpane ), ob->hbox );
	gtk_paned_set_position( GTK_PANED( bfwin->vpane ), ( gint ) ( bfwin->vpane->allocation.height * 0.8 ) );

	ob->lstore = gtk_list_store_new ( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
	ob->lview = gtk_tree_view_new_with_model( GTK_TREE_MODEL( ob->lstore ) );
	g_object_unref( ob->lstore );
	/* the view widget now holds the only reference, if the view is destroyed, the model will be destroyed */

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ( _("Filename"), renderer, "text", 0, NULL );
	gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );
	column = gtk_tree_view_column_new_with_attributes ( _("Line"), renderer, "text", 1, NULL );
	gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );
	column = gtk_tree_view_column_new_with_attributes ( _("Output"), renderer, "markup", 2, NULL );
	gtk_tree_view_append_column ( GTK_TREE_VIEW( ob->lview ), column );

	scrolwin = gtk_scrolled_window_new( NULL, NULL );
	gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolwin ), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
	gtk_container_add( GTK_CONTAINER( scrolwin ), ob->lview );
	gtk_widget_set_size_request( scrolwin, 150, 150 );
	gtk_box_pack_start( GTK_BOX( ob->hbox ), scrolwin, TRUE, TRUE, 0 );
	g_signal_connect( G_OBJECT( ob->lview ), "row-activated", G_CALLBACK( ob_lview_row_activated_lcb ), ob );

	vbox2 = gtk_vbox_new( FALSE, 0 );
	but = gtk_button_new();
	image = new_pixmap( 4 );
	gtk_widget_show( image );
	gtk_container_add( GTK_CONTAINER( but ), image );
	gtk_container_set_border_width( GTK_CONTAINER( but ), 0 );
	gtk_widget_set_size_request( but, 16, 16 );
	g_signal_connect( G_OBJECT( but ), "clicked", G_CALLBACK( outputbox_close_clicked_lcb ), ob );
	gtk_box_pack_start( GTK_BOX( ob->hbox ), vbox2, FALSE, FALSE, 0 );
	gtk_box_pack_start( GTK_BOX( vbox2 ), but, FALSE, FALSE, 0 );

	gtk_widget_show_all( ob->hbox );
	setup_toggle_item_from_widget( bfwin->menubar, N_( "/View/View Outputbox" ), TRUE );
	
	/* create the popup menu */
	/* GtkWidget * menu = create_popup_menu(); */
	
	return ob;
}
/* kyanh, added, 20050227
 Send some specific message to outputbox.
 Please DONOT use for general purpose :!
 
 @markup: should be: b, i, u
*/
static void outputbox_message( Toutputbox *ob, const char *string, const char *markup )
{
	GtkTreeIter iter;
	gchar *tmpstr = g_markup_escape_text(string,-1);
	if (markup) {
		tmpstr = g_strdup_printf("&gt; <%s>%s</%s>", markup, string, markup);
	}else{
		tmpstr = g_strdup_printf("&gt; %s", string);
	}
	gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
	gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmpstr, -1 );
	g_free(tmpstr);

	/* TODO: Scroll as an Optional */
	/* The Outputbox may *NOT* be shown before scrolling :) */
	/* kyanh, added, 20050301 */
	GtkTreePath *treepath = gtk_tree_model_get_path( GTK_TREE_MODEL( ob->lstore ), &iter );
	gtk_tree_view_scroll_to_cell( GTK_TREE_VIEW( ob->lview ), treepath, NULL, FALSE /* skip align */, 0, 0 );
	gtk_tree_path_free( treepath );
}

static void fill_outputbox( Toutputbox *ob, const gchar *source )
{
	GtkTreeIter iter;
	gchar *tmp_src = NULL;
	if ( ob->def->show_all_output ) {
		tmp_src = g_markup_escape_text(source,-1);
		gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
		gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 );
		g_free(tmp_src);
	} else if ( regexec( &ob->def->preg, source, NUM_MATCH, ob->def->pmatch, 0 ) == 0 ) {
		/* we have a valid line */
		gchar * filename, *line, *output;
		filename = line = output = NULL;
		gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
		if ( ob->def->file_subpat >= 0 && ob->def->pmatch[ ob->def->file_subpat ].rm_so >= 0 ) {
			DEBUG_MSG( "fill_outputbox, filename from %d to %d\n", ob->def->pmatch[ ob->def->file_subpat ].rm_so , ob->def->pmatch[ ob->def->file_subpat ].rm_eo );
			filename = g_strndup( &source[ ob->def->pmatch[ ob->def->file_subpat ].rm_so ], ob->def->pmatch[ ob->def->file_subpat ].rm_eo - ob->def->pmatch[ ob->def->file_subpat ].rm_so );
		}
		if ( ob->def->line_subpat >= 0 && ob->def->pmatch[ ob->def->line_subpat ].rm_so >= 0 ) {
			DEBUG_MSG( "fill_outputbox, line from %d to %d\n", ob->def->pmatch[ ob->def->line_subpat ].rm_so , ob->def->pmatch[ ob->def->line_subpat ].rm_eo );
			line = g_strndup( &source[ ob->def->pmatch[ ob->def->line_subpat ].rm_so ], ob->def->pmatch[ ob->def->line_subpat ].rm_eo - ob->def->pmatch[ ob->def->line_subpat ].rm_so );
		}
		if ( ob->def->output_subpat >= 0 && ob->def->pmatch[ ob->def->output_subpat ].rm_so >= 0 ) {
			DEBUG_MSG( "fill_outputbox, output from %d to %d\n", ob->def->pmatch[ ob->def->output_subpat ].rm_so , ob->def->pmatch[ ob->def->output_subpat ].rm_eo );
			output = g_strndup( &source[ ob->def->pmatch[ ob->def->output_subpat ].rm_so ], ob->def->pmatch[ ob->def->output_subpat ].rm_eo - ob->def->pmatch[ ob->def->output_subpat ].rm_so );
		}
		if ( filename ) {
			gchar * fullpath;
			/* create_full_path uses the current directory of no basedir is set */
			/* TODO: better hanlder with full path :) */
			fullpath = create_full_path( filename, NULL );
			gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 0, fullpath, -1 );
			g_free( filename );
			g_free( fullpath );
		}
		if ( line ) {
			gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 1, line, -1 );
			g_free( line );
		}
		if ( output ) {
			tmp_src = g_markup_escape_text(output,-1);
			gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 );
			g_free( output );
			g_free(tmp_src);
		}
	/* TODO: filter function */	
	} else if ( strstr( source, "LaTeX Error" )
					  || strstr( source, "Output written on")
					  || strstr( source, "Warning")
					  || strstr( source, "Overfull")
		  )
	{
		/* kyanh, 20050226,
		special filter for latex *.log file;
		this happens for e.g. when a Package not found (! LaTeX Error: .... )
		*/
		tmp_src = g_markup_escape_text(source,-1);
		gtk_list_store_append( GTK_LIST_STORE( ob->lstore ), &iter );
		gtk_list_store_set( GTK_LIST_STORE( ob->lstore ), &iter, 2, tmp_src, -1 );
		g_free(tmp_src);
	}
}

/* kyanh, added, 20050301 */
static void free_ob( Toutputbox *ob, gboolean have_retfile )
{
	if ( have_retfile ) {
		/* free temporarily file */
		remove_secure_dir_and_filename( ob->retfile );
		DEBUG_MSG( "continue_execute: retfile=%s\n", ob->retfile );
		g_free( ob->retfile );
	}
	g_free( ob->def->pattern );
	regfree( &ob->def->preg );
	g_free( ob->def->command );
	g_free( ob->def );
	ob->def = NULL; /* to be check for next using */
	ob->pid = 0;
}

/* kyanh, 20050302, moved from continute_execute() */
static void finish_execute( Toutputbox *ob )
{
	kill( ob->pid, SIGTERM );
	waitpid( ob->pid, &child_exit_status, WNOHANG );
	{
		gint exitcode = WEXITSTATUS( child_exit_status );
		gchar *str_status = g_strdup_printf(_("Exit code: %d"), exitcode);
		outputbox_message( ob, str_status, "b" );
		g_free( str_status );
	}

	gtk_tree_view_columns_autosize( GTK_TREE_VIEW( ob->lview ) );
	g_io_channel_unref( ob->io_channel );
	gtk_timeout_remove( ob->pollID );
	free_ob( ob, 1 );
}

/* Idea taken from SciTTEGTK.cxx
 Modified by [EMAIL PROTECTED]
 kyanh, version 2 (20050302): use g_io_channel()
 kyanh, version 2.1 (20050315): readline(), not readchars()
*/
static void continue_execute( Toutputbox *ob )
{
	gsize count = 0;
	GIOStatus io_status;
	GError *error = NULL;
	gchar *buf = NULL;
	gsize terminator_pos = 0;
	gboolean continued = TRUE;
	while ( continued ) {
		continued = FALSE;
		buf = NULL;

		io_status = g_io_channel_read_line( ob->io_channel, &buf, &count, &terminator_pos, &error );
		switch ( io_status ) {
		case G_IO_STATUS_ERROR:
			{
				gchar * tmpstr;
				tmpstr = g_strdup_printf( _("IOChannel Error: %s"), error->message );
				outputbox_message( ob, tmpstr, "b" );
				g_free( tmpstr );
				finish_execute( ob );
			}
			break;
		case G_IO_STATUS_EOF: /* without this, we dump into an infinite loop */
			finish_execute( ob );
			break;
		case G_IO_STATUS_NORMAL:
			continued = TRUE;
			if ( terminator_pos < count ) {
				buf[ terminator_pos ] = '\0';
			}
			fill_outputbox( ob, buf );
			break;
		default:
			break;
		}
	}
	g_free( buf );
	g_clear_error( &error );
}

/* continue to read data */
/* written by kyanh, 20050301 */
static void io_signal( GIOChannel *source, GIOCondition condition, Toutputbox *ob )
{
	continue_execute( ob );
}

/* Taken from SciTTEGTK.cxx
Detect if the tool has exited without producing any output
*/
static int poll_tool( Toutputbox *ob )
{
	continue_execute( ob );
	return TRUE;
}

/* Taken from SciTTEGTK.cxx
kyanh: everything emitted from `running' will be captured [ 2>&1 ]
*/
static gint xsystem( const gchar *command, const gchar *outfile )
{
	gint pid = 0;
	/* fork():
	create a child proccess the differs from the parent only in its PID and PPID;
	the resouce ultilisation are set to 0 */
	if ( ( pid = fork() ) == 0 ) {
		close( 0 );
		gint fh = open( outfile, O_WRONLY );
		close( 1 );
		dup( fh ); /* dup uses the lowest?numbered unused descriptor for the new descriptor. */
		close( 2 );
		dup( fh );
		DEBUG_MSG( "xsystem: running now [%s]\n", command );
		execlp( "/bin/sh", "sh", "-c", command, NULL );
		/* Functions that contain the letter l (execl, execlp, and
		execle) accept the argument list using the C language varargs mechanism. */
		/* The execvp function returns only if an error occurs. */
		exit( 127 );
	}
	/* This is the parent process. */
	return pid;
}

static void run_command( Toutputbox *ob )
{
	file_save_cb( NULL, ob->bfwin );
	outputbox_message( ob, ob->def->command, "i" );
	{
		gchar *project_mode;
		if ( main_v->props.project_mode ) {
			project_mode = g_strdup( _("Project Mode: ON") );
		} else {
			project_mode = g_strdup( _("Project Mode: OFF") );
		}
		outputbox_message( ob, project_mode, "i" );
		g_free( project_mode );
	}
	if ( ob->bfwin->current_document->filename ) {
		/* if the user clicked cancel at file_save -> return */
		{
			gchar * tmpstring;
			if ( main_v->props.project_mode && ob->bfwin->project && ob->bfwin->project->basedir )
			{
				tmpstring = g_strdup( ob->bfwin->project->basedir );
			} else
			{
				tmpstring = g_path_get_dirname( ob->bfwin->current_document->filename );
			}
			/* outputbox_message(ob, g_strconcat("> Working dir: ", tmpstring, NULL)); */
			chdir( tmpstring );
			g_free( tmpstring );
		}

		gchar *command = convert_command( ob->bfwin, ob->def->command );
		outputbox_message( ob, command, "b");

		ob->retfile = create_secure_dir_return_filename();

		gint fd = 1;
		if ( ob->retfile ) {
			fd = mkfifo( ob->retfile, S_IRUSR | S_IWUSR );
			if ( fd == 0 ) {
				ob->pid = xsystem( command, ob->retfile );
				GError *error = NULL;
				ob->io_channel = g_io_channel_new_file( ob->retfile, "r", &error );
				if ( ob->io_channel != NULL ) {
					/* Fix the BUGS[200503]#20 */
					g_io_channel_set_encoding( ob->io_channel, NULL, NULL );
					g_io_add_watch( ob->io_channel, G_IO_IN, ( GIOFunc ) io_signal, ob );
					/* add a background task in case there is no output from the tool */
					ob->pollID = g_timeout_add( 200, ( GSourceFunc ) poll_tool, ob );
				} else {
					gchar *tmpstr;
					tmpstr = g_strdup_printf( _("Error: %s"), error->message );
					outputbox_message( ob, tmpstr, "b" );
					if ( error->code == G_FILE_ERROR_INTR ) {
						outputbox_message( ob, _("Hint: You may call the tool again"), "i" );
					}
					g_free( tmpstr );
					outputbox_message( ob, _("Tool finished."), "b" );
					free_ob( ob, 1 );
				}
				g_clear_error( &error );
			}
		}
		if ( fd != 0 ) {
			outputbox_message( ob, _("Error: Cannot create PIPE."), "b" );
			free_ob( ob, 1 );
		}
		g_free( command );
	} else {
		outputbox_message( ob, _("Tool canceled."), "b" );
		free_ob( ob, 0 );
	}
}

void outputbox( Tbfwin *bfwin, gchar *pattern, gint file_subpat, gint line_subpat, gint output_subpat, gchar *command, gboolean show_all_output )
{
	Toutputbox * ob;
	if ( bfwin->outputbox ) {
		ob = OUTPUTBOX( bfwin->outputbox );
		gtk_widget_show_all( ob->hbox ); /* fix BUGS[200503]#24 */
		setup_toggle_item_from_widget(bfwin->menubar, N_("/View/View Outputbox"), TRUE); /* fix BUGS[200503]#25 */
	} else {
		ob = init_outputbox( bfwin );
	}
	if ( ob->pid ) { /* stop older output box */
		outputbox_message( ob, _("Tool is running. Try 'Stop' intead."), "i" );
		return;
		/*
		gchar * tmpstr;
		tmpstr = g_strdup_printf(_("Multiple calls... stopping: %s"), ob->def->command );
		outputbox_message( ob, tmpstr, "i" );
		g_free( tmpstr );
		finish_execute( ob );
		*/
	}
	gtk_list_store_clear( GTK_LIST_STORE( ob->lstore ) );

	ob->def = g_new0( Toutput_def, 1 );
	ob->def->pattern = g_strdup( pattern );
	ob->def->file_subpat = file_subpat;
	ob->def->line_subpat = line_subpat;
	ob->def->output_subpat = output_subpat;
	ob->def->show_all_output = show_all_output;
	regcomp( &ob->def->preg, ob->def->pattern, REG_EXTENDED );
	ob->def->command = g_strdup( command );
	/* kyanh */
	ob->retfile = NULL;
	ob->io_channel = NULL;
	ob->pollID = 0;
	ob->pid = 0;
	run_command( ob );
	/* gtk_widget_show_all(ob->hbox); */
}

Trả lời cho