/* Extension dependent execution.
   Copyright (C) 1994, 1995 The Free Software Foundation
   
   Written by: 1995 Jakub Jelinek
               1994 Miguel de Icaza
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <config.h>
#include <stdio.h> 
#include "tty.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <errno.h>
#include "mad.h"
#include "user.h"
#include "main.h"
#include "fs.h"
#include "util.h"
#include "dialog.h"
#include "global.h"
#include "ext.h"
#include "view.h"
#include "main.h"
#include "../vfs/vfs.h"

/* "$Id: ext.c,v 1.10 1995/02/21 19:05:45 miguel Exp $" */
int use_file_to_check_type = 1;

void exec_extension (char *filename, char *data, char *drops, int *move_dir)
{
    char *file_name;
    FILE *cmd_file;
    int  expand_prefix_found = 0;
    int parameter_found = 0;
    char prompt [80];
    int run_view = 0;
    int def_hex_mode = default_hex_mode, changed_hex_mode = 0;
    int def_nroff_flag = default_nroff_flag, changed_nroff_flag = 0;
    int written_nonspace = 0;
    int is_cd = 0;
    char buffer [1024];
    char *p = 0;
    char *localcopy = NULL;
    time_t localmtime = 0;
    struct stat mystat;
    int    do_local_copy;

    /* Avoid making a local copy if we are doing a cd */
    if (!vfs_file_is_local(filename))
	do_local_copy = 1;
    else
	do_local_copy = 0;
    
    /* Note: this has to be done after the getlocalcopy call,
     * since it uses tmpnam as well
     */
    file_name = strdup (tmpnam (NULL));

    if ((cmd_file = fopen (file_name, "w+")) == NULL){
	message (1, " Error ", " Can't create temporary command file \n %s ",
		 unix_error_string (errno));
	return;
    }
    prompt [0] = 0;
    for (;*data && *data != '\n'; data++){
	if (parameter_found){
	    if (*data == '}'){
		char *parameter;
		parameter_found = 0;
		parameter = input_dialog (" Parameter ", prompt, "");
		if (!parameter){
		    /* User canceled */
		    fclose (cmd_file);
		    unlink (file_name);
		    if (localcopy) {
		        mc_ungetlocalcopy (filename, localcopy, 0);
		    }
		    free (file_name);
		    return;
		}
		fputs (parameter, cmd_file);
		written_nonspace = 1;
		free (parameter);
	    } else {
		int len = strlen (prompt);

		if (len < sizeof (prompt) - 1){
		    prompt [len] = *data;
		    prompt [len+1] = 0;
		}
	    }
	} else if (expand_prefix_found){
	    expand_prefix_found = 0;
	    if (*data == '{')
		parameter_found = 1;
	    else {
	    	int i = check_format_view (data);
		char *v;
		
	    	if (i){
	    	    data += i - 1;
	    	    run_view = 1;
	    	} else if ((i = check_format_cd (data)) > 0) {
	    	    is_cd = 1;
		    do_local_copy = 0;
	    	    p = buffer;
	    	    data += i - 1;
		} else if ((i = check_format_var (data, &v)) > 0 && v){
		    fputs (v, cmd_file);
		    free (v);
		    data += i;
	        } else {
		    char *text;

		    if (*data == 'f' && do_local_copy){
			localcopy = mc_getlocalcopy (filename);
			if (localcopy == NULL) {
			    fclose(cmd_file);
			    unlink(file_name);
			    free(file_name);
			    return;
			}
			mc_stat (localcopy, &mystat);
			localmtime = mystat.st_mtime;
		        text = strdup (localcopy);
		    } else if (*data == 'q') {
		        text = strdup (drops);
		    } else
		        text = expand_format (*data);
		    if (!is_cd)
		        fputs (text, cmd_file);
		    else {
		    	strcpy (p, text);
		    	p = strchr (p, 0);
		    }
		    free (text);
		    written_nonspace = 1;
	        }
	    }
	} else {
	    if (*data == '%')
		expand_prefix_found = 1;
	    else {
	        if (*data != ' ' && *data != '\t')
	            written_nonspace = 1;
	        if (is_cd)
	            *(p++) = *data;
	        else
		    fputc (*data, cmd_file);
	    }
	}
    } /* for */
    fputc ('\n', cmd_file);
    fclose (cmd_file);
    chmod (file_name, S_IRWXU);
    if (run_view){
    	altered_hex_mode = 0;
    	altered_nroff_flag = 0;
    	if (def_hex_mode != default_hex_mode)
    	    changed_hex_mode = 1;
    	if (def_nroff_flag != default_nroff_flag)
    	    changed_nroff_flag = 1;
	
    	/* If we've written whitespace only, then just load filename
	 * into view
	 */
    	if (written_nonspace) 
    	    view (file_name, filename, move_dir);
    	else
    	    view (0, filename, move_dir);
    	if (changed_hex_mode && !altered_hex_mode)
    	    default_hex_mode = def_hex_mode;
    	if (changed_nroff_flag && !altered_nroff_flag)
    	    default_nroff_flag = def_nroff_flag;
    	repaint_screen ();
    } else if (is_cd) {
    	char *q;
    	*p = 0;
    	p = buffer;
    	while (*p == ' ' || *p == '\t')
    	    p++;
    	q = p;
    	while (*q && *q != ' ' && *q != '\t')
    	    q++;
    	*q = 0;
    	do_cd (p);
    } else {
	if (vfs_current_is_local ())
	    shell_execute (file_name, 1);
	else
	    message (1, " Warning ", " Can't execute commands on a Virtual File System directory ");
    }
    unlink (file_name);
    if (localcopy) {
        mc_stat (localcopy, &mystat);
        mc_ungetlocalcopy (filename, localcopy, localmtime != mystat.st_mtime);
    }
    free (file_name);
}

#ifdef FILE_L
#   define FILE_CMD "file -L "
#else
#   define FILE_CMD "file "
#endif

/* The second argument is action, i.e. Open, View, Edit, Drop, or NULL if
 * we want regex_command to return a list of all user defined actions.
 * Third argument is space separated list of dropped files (for actions
 * other then Drop it should be NULL);
 *
 * This function returns:
 *
 * If action != NULL, then it returns "Success" (not allocated) if it ran
 * some command or NULL if not.
 *
 * If action == NULL, it returns NULL if there are no user defined commands
 * or an allocated space separated list of user defined Actions.
 *
 * If action == "Icon", we are doing again something special. We return
 * icon name and we set the variable regex_command_title to Title for
 * that icon.
 */
static char *data = NULL;
char *regex_command_title = NULL;
char *regex_command (char *filename, char *action, char *drops, int *move_dir)
{
    char *extension_file;
    char *p, *q, *r, c;
    char *buffer;
    int  file_len = strlen (filename);
    int found = 0;
    char content_string [2048];
    int content_shift = 0;
    char *to_return = NULL;
    int old_patterns;
    struct stat mystat;
    int asked_file;

#ifdef FILE_STDIN
    int file_supports_stdin = 1;
#else
    int file_supports_stdin = 0;
#endif    
    
    /* Have we asked file for the file contents? */
    asked_file = 0;

    if (data == NULL) {
        int home_error = 0;
        
    	buffer = copy_strings (home_dir, PATH_SEP_STR ".mc.ext", 0);
    	if (exist_file (buffer))
	    extension_file = buffer;
        else
check_stock_mc_ext:
	    extension_file = LIBDIR "mc.ext";
        if ((data = load_file (extension_file)) == NULL) {
	    free (buffer);
	    return 0;
	}
	if (!strstr (data, "default/")) {
	    if (!strstr (data, "regex/") && !strstr (data, "shell/") &&
	        !strstr (data, "type/")) {
	        free (data);
	        data = NULL;
	        if (extension_file == buffer) {
		    home_error = 1;
		    goto check_stock_mc_ext;            
	        } else {
	            message (1, " " LIBDIR "mc.ext file error", "\
Format of the " LIBDIR "mc.ext file has changed\n\
with version 3.0. It seems that installation\n\
failed. Please fetch a fresh new copy from the\n\
Midnight Commander package or in case you don't\n\
have any, get it from ftp://ftp.nuclecu.unam.mx.");
		    free (buffer);
		    return 0;
	        }
	    }
	}
	if (home_error)
	    message (1, " ~/.mc.ext file error ", "\
Format of the ~/.mc.ext file has changed\n\
with version 3.0. You may want either to\n\
copy it from " LIBDIR "mc.ext or use that\n\
file as an example of how to write it.\n\
" LIBDIR "mc.ext will be used for this moment.");
        free (buffer);
    }
    mc_stat (filename, &mystat);
    
    regex_command_title = NULL;
    old_patterns = easy_patterns;
    easy_patterns = 0; /* Real regular expressions are needed :) */
    for (p = data; *p; p++) {
    	for (q = p; *q == ' ' || *q == '\t'; q++);
    	if (*q == '\n' || !*q)
    	    p = q; /* empty line */
    	if (*p == '#') /* comment */
    	    while (*p && *p != '\n')
    	    	p++;
	if (*p == '\n')
	    continue;
	if (!*p)
	    break;
	if (p == q) { /* i.e. starts in the first column, should be
	               * keyword/descNL
	               */
	    if (found && action == NULL) /* We have already accumulated all
	    				  * the user actions 
	    				  */
	        break;
	    found = 0;
	    q = strchr (p, '\n'); 
	    if (q == NULL)
	        q = strchr (p, 0);
	    c = *q;
	    *q = 0;
	    if (!strncmp (p, "regex/", 6)) {
	        p += 6;
	        /* Do not transform shell patterns, you can use shell/ for
	         * that
	         */
	        if (regexp_match (p, filename, match_normal))
	            found = 1;
	    } else if (!strncmp (p, "directory/", 10)) {
	        if (S_ISDIR (mystat.st_mode) && regexp_match (p, filename, match_normal))
	            found = 1;
	    } else if (!strncmp (p, "shell/", 6)) {
	        p += 6;
	        if (*p == '.') {
	            if (!strncmp (p, filename + file_len - (q - p), 
	                q - p))
	                found = 1;
	        } else {
	            if (q - p == file_len && !strncmp (p, filename, q - p))
	                found = 1;
	        }
	    } else if (!strncmp (p, "type/", 5)) {
		int islocal = vfs_file_is_local (filename);
	        p += 5;
		
	        if (islocal || file_supports_stdin) {
	    	    char *pp;
	    	    int hasread = use_file_to_check_type;

		    if (asked_file || !use_file_to_check_type)
			goto match_file_output;

		    hasread = 0;
	    	    if (islocal) {
	    	        char *command =
			    copy_strings (FILE_CMD, "'", filename, "'", NULL);
	    	        FILE *f = popen (command, "r");
	    	    
	    	        free (command);
	    	        if (f != NULL) {
	    	            hasread = (fgets (content_string, 2047, f) 
	    	                != NULL);
	    	    	    if (!hasread)
	    	    	        content_string [0] = 0;
	    	    	    pclose (f);
	    	        }
	    	    } else {
#ifdef _OS_NT
			message (1, " NT: Unimplemented file prediction ");
#else
	    	        int pipehandle, remotehandle;
	    	        pid_t p;
		    
	    	        remotehandle = mc_open (filename, O_RDONLY);
		        if (remotehandle != -1) {
		        /* 8192 is HOWMANY hardcoded value in the file-3.14
		         * sources. Tell me if any other file uses larger
		         * chunk from beginning 
		         */
	    	            pipehandle = mc_doublepopen
			    (remotehandle, 8192, &p,"file", "file", "-", NULL);
			    if (pipehandle != -1) {
	    	                int i;
	    	                while ((i = read (pipehandle, content_string 
	    	                     + hasread, 2047 - hasread)) > 0)
	    	                    hasread += i;
	    	    	        mc_doublepclose (pipehandle, p);
	    	    	        content_string [hasread] = 0;
	    	            }
	    	            mc_close (remotehandle);
	    	        }
#endif /* _OS_NT */
	    	    }
		    asked_file = 1;
match_file_output:
	    	    if (hasread) {
	    	        if ((pp = strchr (content_string, '\n')) != 0)
	    	    	    *pp = 0;
	    	        if (islocal && !strncmp (content_string, 
	    	            filename, file_len)) {
	    	    	    content_shift = file_len;
	    	    	    if (content_string [content_shift] == ':')
	    	    	        for (content_shift++; 
	    	    	            content_string [content_shift] == ' '; 
	    	    	            content_shift++);
	    	        } else if (!islocal 
				   && !strncmp (content_string, 
						"standard input:", 15)) {
	    	            for (content_shift = 15;
	    	                content_string [content_shift] == ' ';
	    	                content_shift++);
	    	        }
	    		if (content_string && 
	    		    regexp_match (p, content_string + 
	    		        content_shift, match_normal)){
	    		    found = 1;
	    		}
	    	    }
	        }
	    } else if (!strncmp (p, "default/", 8)) {
	        p += 8;
	        found = 1;
	    }
	    *q = c;
	    p = q;
	    if (!*p)
	        break;
    	} else { /* List of actions */
    	    p = q;
    	    q = strchr (p, '\n');
    	    if (q == NULL)
    	        q = strchr (p, 0);
    	    if (found) {
    	        r = strchr (p, '=');
    	        if (r != NULL) {
    	            c = *r;
    	            *r = 0;
    	            if (action == NULL) {
    	                if (strcmp (p, "Open") && 
    	                    strcmp (p, "View") &&
    	                    strcmp (p, "Edit") &&
    	                    strcmp (p, "Drop") &&
    	                    strcmp (p, "Icon") &&
    	                    strcmp (p, "Title")) {
    	                    /* I.e. this is a name of a user defined action */
    	                        static char *q;
    	                        
    	                        if (to_return == NULL) {
    	                            to_return = xmalloc (512, "Action list");
    	                            q = to_return;
    	                        } else 
    	                            *(q++) = ' ';
    	                        strcpy (q, p);
    	                        q = strchr (q, 0);
    	                }
    	                *r = c;
    	            } else if (!strcmp (action, "Icon")) {
    	                if (!strcmp (p, "Icon") && to_return == NULL) {
    	            	    *r = c;
    	            	    c = *q;
    	            	    *q = 0;
    	                    to_return = strdup (r + 1);
    	                } else if (!strcmp (p, "Title") && regex_command_title == NULL) {
    	            	    *r = c;
    	            	    c = *q;
    	            	    *q = 0;
    	                    regex_command_title = strdup (r + 1);
    	                } else {
    	                    *r = c;
    	                    c = *q;
    	                }
    	                *q = c;
    	                if (to_return != NULL && regex_command_title != NULL)
    	                    break;
    	            } else if (!strcmp (action, p)) {
    	                *r = c;
    	                for (p = r + 1; *p == ' ' || *p == '\t'; p++);
    	                if (p < q) { /* Empty commands just stop searching
    	                              * through, they don't do anything
    	                              */
    	                    exec_extension (filename, r + 1, drops, move_dir);
    	                    to_return = "Success";
    	                }
    	                break;
    	            } else
    	            	*r = c;
    	        }
    	    }
    	    p = q;
    	    if (!*p)
    	        break;
    	}
    }
    easy_patterns = old_patterns;
    return to_return;
}
