2005-09-20 23:26:39 +10:00
/** \file mimedb.c
mimedb is a program for checking the mimetype , description and
default action associated with a file or mimetype . It uses the
xdgmime library written by the fine folks at freedesktop . org . There does
not seem to be any standard way for the user to change the preferred
application yet .
The first implementation of mimedb used xml_grep to parse the xml
file for the mime entry to determine the description . This was abandoned
because of the performance implications of parsing xml . The current
version only does a simple string search , which is much , much
faster but it might fall on it ' s head .
This code is Copyright 2005 Axel Liljencrantz .
It is released under the GPL .
The xdgmime library is dual licensed under LGPL / artistic
license . Read the source code of the library for more information .
*/
# include "config.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <fcntl.h>
# include <libgen.h>
# include <errno.h>
2006-01-24 01:45:00 +10:00
# include <regex.h>
# include <locale.h>
2005-09-20 23:26:39 +10:00
# ifdef HAVE_GETOPT_H
# include <getopt.h>
# endif
2006-01-24 01:45:00 +10:00
# if HAVE_LIBINTL_H
# include <libintl.h>
# endif
2005-09-20 23:26:39 +10:00
# include "xdgmime.h"
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
/**
Location of the applications . desktop file , relative to a base mime directory
*/
# define APPLICATIONS_DIR "applications / "
/**
Location of the mime xml database , relative to a base mime directory
*/
# define MIME_DIR "mime / "
/**
Filename suffix for XML files
*/
# define MIME_SUFFIX ".xml"
/**
2006-01-24 01:45:00 +10:00
Start tag for langauge - specific comment
2005-09-20 23:26:39 +10:00
*/
2006-01-24 01:45:00 +10:00
# define START_TAG "<comment *(| +xml:lang *= *(\"%s\"|'%s') *)>"
2005-09-20 23:26:39 +10:00
/**
End tab for comment
*/
2006-01-24 01:45:00 +10:00
# define STOP_TAG "< / comment *>"
2005-09-20 23:26:39 +10:00
/**
File contains cached list of mime actions
*/
# define DESKTOP_DEFAULT "applications / defaults.list"
2006-01-24 01:45:00 +10:00
/**
Size for temporary string buffer used to make a regex for language
specific descriptions
*/
# define BUFF_SIZE 1024
2006-01-24 06:40:14 +10:00
/**
2006-01-24 01:45:00 +10:00
Program name
*/
# define MIMEDB "mimedb"
2005-09-20 23:26:39 +10:00
2006-05-27 00:55:51 +10:00
/**
Getopt short switches for mimedb
*/
# define GETOPT_STRING "tfimdalhv"
2005-09-20 23:26:39 +10:00
/**
All types of input and output possible
*/
enum
{
FILEDATA ,
FILENAME ,
MIMETYPE ,
DESCRIPTION ,
ACTION ,
LAUNCH
}
;
2006-01-24 06:40:14 +10:00
/**
Regular expression variable used to find start tag of description
*/
static regex_t * start_re = 0 ;
/**
Regular expression variable used to find end tag of description
*/
static regex_t * stop_re = 0 ;
2006-01-24 01:45:00 +10:00
2005-09-20 23:26:39 +10:00
/**
Error flag . Non - zero if something bad happened .
*/
static int error = 0 ;
/**
String of characters to send to system ( ) to launch a file
*/
static char * launch_buff = 0 ;
/**
Length of the launch_buff buffer
*/
static int launch_len = 0 ;
/**
Current position in the launch_buff buffer
*/
static int launch_pos = 0 ;
2006-01-24 01:45:00 +10:00
# if HAVE_GETTEXT
2006-01-24 06:40:14 +10:00
/**
gettext alias
*/
2006-01-24 01:45:00 +10:00
# define _(string) gettext(string)
# else
# define _(string) (string)
# endif
2005-09-20 23:26:39 +10:00
/**
Dynamically generated function , made from the documentation in doc_src .
*/
void print_help ( ) ;
/**
Call malloc , set error flag and print message on failure
*/
void * my_malloc ( size_t s )
{
void * res = malloc ( s ) ;
if ( ! s )
{
error = 1 ;
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Out of memory \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
}
return res ;
}
/**
Duplicate string , set error flag and print message on failure
*/
char * my_strdup ( char * s )
{
char * res = strdup ( s ) ;
if ( ! s )
{
error = 1 ;
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Out of memory \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
}
return res ;
}
/**
Search the file \ c filename for the first line starting with \ c
match , which is returned in a newly allocated string .
*/
static char * search_ini ( const char * filename , const char * match )
{
FILE * f = fopen ( filename , " r " ) ;
char buf [ 4096 ] ;
int len = strlen ( match ) ;
int done = 0 ;
if ( ! f )
{
perror ( " fopen " ) ;
error = 1 ;
return 0 ;
}
while ( ! done )
{
if ( ! fgets ( buf , 4096 , f ) )
{
if ( ! feof ( f ) )
{
perror ( " fgets " ) ;
error = 1 ;
}
buf [ 0 ] = 0 ;
done = 1 ;
}
else if ( strncmp ( buf , match , len ) = = 0 )
{
done = 1 ;
}
}
fclose ( f ) ;
if ( buf [ 0 ] )
{
char * res = strdup ( buf ) ;
if ( res )
{
if ( res [ strlen ( res ) - 1 ] = = ' \n ' )
res [ strlen ( res ) - 1 ] = ' \0 ' ;
}
return res ;
}
else
return ( char * ) 0 ;
}
/**
Test if the specified file exists . If it does not , also try
replacing dashes with slashes in \ c in .
*/
static char * file_exists ( const char * dir , const char * in )
{
char * filename = my_malloc ( strlen ( dir ) + strlen ( in ) + 1 ) ;
char * replaceme ;
struct stat buf ;
// fprintf( stderr, "Check %s%s\n", dir, in );
if ( ! filename )
{
return 0 ;
}
strcpy ( filename , dir ) ;
strcat ( filename , in ) ;
if ( ! stat ( filename , & buf ) )
return filename ;
free ( filename ) ;
/*
DOH ! File does not exist . But all is not lost . KDE sometimes uses
a slash in the name as a directory separator . We try to replace
a dash with a slash and try again .
*/
replaceme = strchr ( in , ' - ' ) ;
if ( replaceme )
{
char * res ;
* replaceme = ' / ' ;
res = file_exists ( dir , in ) ;
* replaceme = ' - ' ;
return res ;
}
/*
OK , no more slashes left . We really are screwed . Nothing to to
but admit defeat and go home .
*/
return 0 ;
}
/**
Try to find the specified file in any of the possible directories
where mime files can be located . This code is shamelessly stolen
from xdg_run_command_on_dirs .
*/
static char * get_filename ( char * f )
{
char * result ;
const char * xdg_data_home ;
const char * xdg_data_dirs ;
const char * ptr ;
xdg_data_home = getenv ( " XDG_DATA_HOME " ) ;
if ( xdg_data_home )
{
result = file_exists ( xdg_data_home , f ) ;
if ( result )
return result ;
}
else
{
const char * home ;
home = getenv ( " HOME " ) ;
if ( home ! = NULL )
{
char * guessed_xdg_home ;
guessed_xdg_home = my_malloc ( strlen ( home ) + strlen ( " /.local/share/ " ) + 1 ) ;
if ( ! guessed_xdg_home )
return 0 ;
strcpy ( guessed_xdg_home , home ) ;
strcat ( guessed_xdg_home , " /.local/share/ " ) ;
result = file_exists ( guessed_xdg_home , f ) ;
free ( guessed_xdg_home ) ;
if ( result )
return result ;
}
}
xdg_data_dirs = getenv ( " XDG_DATA_DIRS " ) ;
if ( xdg_data_dirs = = NULL )
xdg_data_dirs = " /usr/local/share/:/usr/share/ " ;
ptr = xdg_data_dirs ;
while ( * ptr ! = ' \000 ' )
{
const char * end_ptr ;
char * dir ;
int len ;
end_ptr = ptr ;
while ( * end_ptr ! = ' : ' & & * end_ptr ! = ' \000 ' )
end_ptr + + ;
if ( end_ptr = = ptr )
{
ptr + + ;
continue ;
}
if ( * end_ptr = = ' : ' )
len = end_ptr - ptr ;
else
len = end_ptr - ptr + 1 ;
dir = my_malloc ( len + 1 ) ;
if ( ! dir )
return 0 ;
strncpy ( dir , ptr , len ) ;
dir [ len ] = ' \0 ' ;
result = file_exists ( dir , f ) ;
free ( dir ) ;
if ( result )
return result ;
ptr = end_ptr ;
}
return 0 ;
}
/**
Remove excessive whitespace from string . Replaces arbitrary sequence
of whitespace with a single space . Also removes any leading and
trailing whitespace
*/
static char * munge ( char * in )
{
char * out = my_malloc ( strlen ( in ) + 1 ) ;
char * p = out ;
int had_whitespace = 0 ;
int printed = 0 ;
if ( ! out )
{
return 0 ;
}
while ( 1 )
{
// fprintf( stderr, "%c\n", *in );
switch ( * in )
{
case ' ' :
case ' \n ' :
case ' \t ' :
case ' \r ' :
{
had_whitespace = 1 ;
break ;
}
case ' \0 ' :
* p = ' \0 ' ;
return out ;
default :
{
if ( printed & & had_whitespace )
{
* ( p + + ) = ' ' ;
}
printed = 1 ;
had_whitespace = 0 ;
* ( p + + ) = * in ;
break ;
}
}
in + + ;
}
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Unknown error in munge() \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
error = 1 ;
return 0 ;
}
2006-01-24 01:45:00 +10:00
/**
Return a regular expression that matches all strings specifying the current locale
*/
static char * get_lang_re ( )
{
static char buff [ BUFF_SIZE ] ;
const char * lang = setlocale ( LC_MESSAGES , 0 ) ;
int close = 0 ;
char * out = buff ;
if ( ( 1 + strlen ( lang ) * 4 ) > = BUFF_SIZE )
{
fprintf ( stderr , _ ( " %s: Locale string too long \n " ) , MIMEDB ) ;
error = 1 ;
return 0 ;
}
for ( ; * lang ; lang + + )
{
switch ( * lang )
{
case ' @ ' :
case ' . ' :
case ' _ ' :
if ( close )
* out + + = ' ) ' ;
close = 1 ;
* out + + = ' ( ' ;
* out + + = ' | ' ;
* out + + = * lang ;
break ;
default :
* out + + = * lang ;
}
}
if ( close )
* out + + = ' ) ' ;
* out + + = 0 ;
return buff ;
}
2005-09-20 23:26:39 +10:00
/**
Get description for a specified mimetype .
*/
static char * get_description ( const char * mimetype )
{
char * fn_part ;
char * fn ;
int fd ;
struct stat st ;
char * contents ;
2006-01-24 01:45:00 +10:00
char * start = 0 , * stop = 0 ;
if ( ! start_re )
{
char * lang ;
char buff [ BUFF_SIZE ] ;
2005-09-20 23:26:39 +10:00
2006-01-24 01:45:00 +10:00
lang = get_lang_re ( ) ;
if ( ! lang )
return 0 ;
snprintf ( buff , BUFF_SIZE , START_TAG , lang , lang ) ;
start_re = my_malloc ( sizeof ( regex_t ) ) ;
stop_re = my_malloc ( sizeof ( regex_t ) ) ;
if ( regcomp ( start_re , buff , REG_EXTENDED ) | |
regcomp ( stop_re , STOP_TAG , REG_EXTENDED ) )
{
fprintf ( stderr , _ ( " %s: Could not compile regular expressions \n " ) , MIMEDB ) ;
error = 1 ;
free ( start_re ) ;
free ( stop_re ) ;
start_re = stop_re = 0 ;
return 0 ;
2006-01-24 06:40:14 +10:00
}
2006-01-24 01:45:00 +10:00
}
2005-09-20 23:26:39 +10:00
fn_part = my_malloc ( strlen ( MIME_DIR ) + strlen ( mimetype ) + strlen ( MIME_SUFFIX ) + 1 ) ;
if ( ! fn_part )
{
return 0 ;
}
strcpy ( fn_part , MIME_DIR ) ;
strcat ( fn_part , mimetype ) ;
strcat ( fn_part , MIME_SUFFIX ) ;
fn = get_filename ( fn_part ) ; //malloc( strlen(MIME_DIR) +strlen( MIME_SUFFIX)+ strlen( mimetype ) + 1 );
free ( fn_part ) ;
if ( ! fn )
{
return 0 ;
}
fd = open ( fn , O_RDONLY ) ;
// fprintf( stderr, "%s\n", fn );
if ( fd = = - 1 )
{
perror ( " open " ) ;
error = 1 ;
return 0 ;
}
if ( stat ( fn , & st ) )
{
perror ( " stat " ) ;
error = 1 ;
return 0 ;
}
contents = my_malloc ( st . st_size + 1 ) ;
if ( ! contents )
{
return 0 ;
}
if ( read ( fd , contents , st . st_size ) ! = st . st_size )
{
perror ( " read " ) ;
error = 1 ;
return 0 ;
}
2006-06-21 19:54:30 +10:00
/*
Don ' t need to check exit status of close on read - only file descriptors
*/
2005-09-20 23:26:39 +10:00
close ( fd ) ;
free ( fn ) ;
contents [ st . st_size ] = 0 ;
2006-01-24 01:45:00 +10:00
regmatch_t match [ 1 ] ;
int w = - 1 ;
start = contents ;
/*
On multiple matches , use the longest match , should be a pretty
good heuristic for best match . . .
*/
while ( ! regexec ( start_re , start , 1 , match , 0 ) )
2005-09-20 23:26:39 +10:00
{
2006-01-24 01:45:00 +10:00
int new_w = match [ 0 ] . rm_eo - match [ 0 ] . rm_so ;
if ( new_w > w )
2005-09-20 23:26:39 +10:00
{
2006-01-24 01:45:00 +10:00
/*
New match is for a longer match then the previous
match , so we use the new match
*/
w = new_w ;
start + = match [ 0 ] . rm_eo ;
}
}
if ( w ! = - 1 )
{
if ( ! regexec ( stop_re , start , 1 , match , 0 ) )
{
/*
We ' ve found the beginning and the end of a suitable description
*/
2005-09-20 23:26:39 +10:00
char * res ;
2006-01-24 01:45:00 +10:00
stop = start + match [ 0 ] . rm_so ;
2005-09-20 23:26:39 +10:00
* stop = ' \0 ' ;
res = munge ( start ) ;
free ( contents ) ;
return res ;
}
}
free ( contents ) ;
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: No description for type %s \n " ) , MIMEDB , mimetype ) ;
2005-09-20 23:26:39 +10:00
error = 1 ;
return 0 ;
}
/**
Get default action for a specified mimetype .
*/
static char * get_action ( const char * mimetype )
{
char * res = 0 ;
char * launcher ;
char * end ;
char * mime_filename ;
char * launcher_str ;
char * launcher_filename , * launcher_command_str , * launcher_command ;
char * launcher_full ;
mime_filename = get_filename ( DESKTOP_DEFAULT ) ;
if ( ! mime_filename )
return 0 ;
launcher_str = search_ini ( mime_filename , mimetype ) ;
free ( mime_filename ) ;
if ( ! launcher_str )
{
/*
This type does not have a launcher . Try the supertype !
*/
// fprintf( stderr, "mimedb: %s does not have launcher, try supertype\n", mimetype );
const char * * parents = xdg_mime_get_mime_parents ( mimetype ) ;
const char * * p ;
if ( parents )
{
for ( p = parents ; * p ; p + + )
{
char * a = get_action ( * p ) ;
if ( a ! = 0 )
return a ;
}
}
/*
Just in case subclassing doesn ' t work , ( It doesn ' t on Fedora
Core 3 ) we also test some common subclassings .
*/
if ( strncmp ( mimetype , " text/ " , 5 ) = = 0 )
return get_action ( " text/plain " ) ;
return 0 ;
}
// fprintf( stderr, "WOOT %s\n", launcher_str );
launcher = strchr ( launcher_str , ' = ' ) ;
if ( ! launcher )
{
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Could not parse launcher string '%s' \n " ) , MIMEDB , launcher_str ) ;
2005-09-20 23:26:39 +10:00
error = 1 ;
return 0 ;
}
/* Skip the = */
launcher + + ;
/* Only use first launcher */
end = strchr ( launcher , ' ; ' ) ;
if ( end )
* end = ' \0 ' ;
launcher_full = my_malloc ( strlen ( launcher ) + strlen ( APPLICATIONS_DIR ) + 1 ) ;
if ( ! launcher_full )
{
free ( launcher_str ) ;
return 0 ;
}
strcpy ( launcher_full , APPLICATIONS_DIR ) ;
strcat ( launcher_full , launcher ) ;
free ( launcher_str ) ;
launcher_filename = get_filename ( launcher_full ) ;
free ( launcher_full ) ;
launcher_command_str = search_ini ( launcher_filename , " Exec= " ) ;
if ( ! launcher_command_str )
{
fprintf ( stderr ,
2006-01-24 01:45:00 +10:00
_ ( " %s: Default launcher '%s' does not specify how to start \n " ) , MIMEDB ,
2005-09-20 23:26:39 +10:00
launcher_filename ) ;
free ( launcher_filename ) ;
return 0 ;
}
free ( launcher_filename ) ;
launcher_command = strchr ( launcher_command_str , ' = ' ) ;
launcher_command + + ;
res = my_strdup ( launcher_command ) ;
free ( launcher_command_str ) ;
return res ;
}
/**
Helper function for launch . Write the specified byte to the string we will execute
*/
static void writer ( char c )
{
if ( launch_len = = - 1 )
return ;
if ( launch_len < = launch_pos )
{
int new_len = launch_len ? 2 * launch_len : 256 ;
char * new_buff = realloc ( launch_buff , new_len ) ;
if ( ! new_buff )
{
free ( launch_buff ) ;
launch_len = - 1 ;
error = 1 ;
return ;
}
launch_buff = new_buff ;
launch_len = new_len ;
}
launch_buff [ launch_pos + + ] = c ;
}
/**
Write out the specified byte in hex
*/
static void writer_hex ( int num )
{
int a , b ;
a = num / 16 ;
b = num % 16 ;
writer ( a > 9 ? ( ' A ' + a - 10 ) : ( ' 0 ' + a ) ) ;
writer ( b > 9 ? ( ' A ' + b - 10 ) : ( ' 0 ' + b ) ) ;
}
/**
Return current directory in newly allocated string
*/
static char * my_getcwd ( )
{
size_t size = 100 ;
while ( 1 )
{
char * buffer = ( char * ) malloc ( size ) ;
if ( getcwd ( buffer , size ) = = buffer )
return buffer ;
free ( buffer ) ;
if ( errno ! = ERANGE )
return 0 ;
size * = 2 ;
}
}
/**
Return absolute filename of specified file
*/
static char * get_fullfile ( char * file )
{
char * fullfile ;
if ( file [ 0 ] = = ' / ' )
{
fullfile = file ;
}
else
{
char * cwd = my_getcwd ( ) ;
if ( ! cwd )
{
error = 1 ;
perror ( " getcwd " ) ;
return 0 ;
}
int l = strlen ( cwd ) ;
fullfile = my_malloc ( l + strlen ( file ) + 2 ) ;
if ( ! fullfile )
{
free ( cwd ) ;
return 0 ;
}
strcpy ( fullfile , cwd ) ;
if ( cwd [ l - 1 ] ! = ' / ' )
strcat ( fullfile , " / " ) ;
strcat ( fullfile , file ) ;
free ( cwd ) ;
}
return fullfile ;
}
/**
Write specified file as an URL
*/
static void write_url ( char * file )
{
char * fullfile = get_fullfile ( file ) ;
char * str = fullfile ;
if ( str = = 0 )
{
launch_len = - 1 ;
return ;
}
writer ( ' f ' ) ;
writer ( ' i ' ) ;
writer ( ' l ' ) ;
writer ( ' e ' ) ;
writer ( ' : ' ) ;
writer ( ' / ' ) ;
writer ( ' / ' ) ;
while ( * str )
{
if ( ( ( * str > = ' a ' ) & & ( * str < = ' z ' ) ) | |
( ( * str > = ' A ' ) & & ( * str < = ' Z ' ) ) | |
( ( * str > = ' 0 ' ) & & ( * str < = ' 9 ' ) ) | |
( strchr ( " ./_ " , * str ) ! = 0 ) )
{
writer ( * str ) ;
}
else if ( strchr ( " ()?&= " , * str ) ! = 0 )
{
writer ( ' \\ ' ) ;
writer ( * str ) ;
}
else
{
writer ( ' % ' ) ;
writer_hex ( * str ) ;
}
str + + ;
}
if ( fullfile ! = file )
free ( fullfile ) ;
}
/**
Write specified file
*/
static void write_file ( char * file , int print_path )
{
char * fullfile ;
char * str ;
if ( print_path )
{
fullfile = get_fullfile ( file ) ;
str = fullfile ;
}
else
{
fullfile = my_strdup ( file ) ;
if ( ! fullfile )
{
return ;
}
str = basename ( fullfile ) ;
}
if ( ! str )
{
error = 1 ;
return ;
}
while ( * str )
{
switch ( * str )
{
case ' ) ' :
case ' ( ' :
case ' - ' :
case ' # ' :
case ' $ ' :
case ' } ' :
case ' { ' :
case ' ] ' :
case ' [ ' :
case ' * ' :
case ' ? ' :
case ' ' :
case ' | ' :
case ' < ' :
case ' > ' :
case ' ^ ' :
case ' & ' :
case ' \\ ' :
case ' ` ' :
case ' \' ' :
case ' \" ' :
writer ( ' \\ ' ) ;
writer ( * str ) ;
break ;
case ' \n ' :
writer ( ' \\ ' ) ;
writer ( ' n ' ) ;
break ;
case ' \r ' :
writer ( ' \\ ' ) ;
writer ( ' r ' ) ;
break ;
case ' \t ' :
writer ( ' \\ ' ) ;
writer ( ' t ' ) ;
break ;
case ' \b ' :
writer ( ' \\ ' ) ;
writer ( ' b ' ) ;
break ;
case ' \v ' :
writer ( ' \\ ' ) ;
writer ( ' v ' ) ;
break ;
default :
writer ( * str ) ;
break ;
}
str + + ;
}
if ( fullfile ! = file )
free ( fullfile ) ;
}
/**
Use the specified launch filter to launch all the files in the specified list .
\ param filter the action to take
\ param files the list of files for which to perform the action
\ param fileno an internal value . Should always be set to zero .
*/
static void launch ( char * filter , array_list_t * files , int fileno )
{
char * filter_org = filter ;
int count = 0 ;
int launch_again = 0 ;
if ( al_get_count ( files ) < = fileno )
return ;
launch_pos = 0 ;
for ( ; * filter & & ! error ; filter + + )
{
if ( * filter = = ' % ' )
{
filter + + ;
switch ( * filter )
{
case ' u ' :
{
launch_again = 1 ;
write_url ( ( char * ) al_get ( files , fileno ) ) ;
break ;
}
case ' U ' :
{
int i ;
for ( i = 0 ; i < al_get_count ( files ) ; i + + )
{
if ( i ! = 0 )
writer ( ' ' ) ;
write_url ( ( char * ) al_get ( files , i ) ) ;
if ( error )
break ;
}
break ;
}
case ' f ' :
case ' n ' :
{
launch_again = 1 ;
write_file ( ( char * ) al_get ( files , fileno ) , * filter = = ' f ' ) ;
break ;
}
case ' F ' :
case ' N ' :
{
int i ;
for ( i = 0 ; i < al_get_count ( files ) ; i + + )
{
if ( i ! = 0 )
writer ( ' ' ) ;
write_file ( ( char * ) al_get ( files , i ) , * filter = = ' F ' ) ;
if ( error )
break ;
}
break ;
}
case ' d ' :
{
char * cpy = get_fullfile ( ( char * ) al_get ( files , fileno ) ) ;
char * dir ;
launch_again = 1 ;
/*
We wish to modify this string , make sure it is only a copy
*/
if ( cpy = = al_get ( files , fileno ) )
cpy = my_strdup ( cpy ) ;
if ( cpy = = 0 )
{
break ;
}
dir = dirname ( cpy ) ;
write_file ( dir , 1 ) ;
free ( cpy ) ;
break ;
}
case ' D ' :
{
int i ;
for ( i = 0 ; i < al_get_count ( files ) ; i + + )
{
char * cpy = get_fullfile ( ( char * ) al_get ( files , i ) ) ;
char * dir ;
/*
We wish to modify this string , make sure it is only a copy
*/
if ( cpy = = al_get ( files , i ) )
cpy = my_strdup ( cpy ) ;
if ( cpy = = 0 )
{
break ;
}
dir = dirname ( cpy ) ;
if ( i ! = 0 )
writer ( ' ' ) ;
write_file ( dir , 1 ) ;
free ( cpy ) ;
}
break ;
}
default :
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Unsupported switch '%c' in launch string '%s' \n " ) , MIMEDB , * filter , filter_org ) ;
2005-09-20 23:26:39 +10:00
launch_len = 0 ;
break ;
}
}
else
{
writer ( * filter ) ;
count + + ;
}
}
if ( error )
return ;
switch ( launch_len )
{
case - 1 :
{
launch_len = 0 ;
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Out of memory \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
return ;
}
case 0 :
{
return ;
}
default :
{
writer ( ' ' ) ;
writer ( ' & ' ) ;
writer ( ' \0 ' ) ;
// fprintf( stderr, "mimedb: %s\n", launch_buff );
system ( launch_buff ) ;
break ;
}
}
if ( launch_again )
{
launch ( filter_org , files , fileno + 1 ) ;
}
}
/**
Clean up one entry from the hash table of launch files
*/
2006-06-13 07:47:42 +10:00
static void clear_entry ( void * key , void * val )
2005-09-20 23:26:39 +10:00
{
/*
The key is a mime value , either from the libraries internal hash
table of mime types or from the command line . Either way , it
should not be freed .
The value is an array_list_t of filenames . The filenames com from
the argument list and should not be freed . The arraylist ,
however , should be destroyed and freed .
*/
array_list_t * l = ( array_list_t * ) val ;
al_destroy ( l ) ;
free ( l ) ;
}
2006-01-24 06:40:14 +10:00
/**
Do locale specific init
*/
2006-01-24 01:45:00 +10:00
static void locale_init ( )
{
setlocale ( LC_ALL , " " ) ;
# if HAVE_GETTEXT
bindtextdomain ( PACKAGE_NAME , LOCALEDIR ) ;
textdomain ( PACKAGE_NAME ) ;
# endif
}
2005-09-20 23:26:39 +10:00
/**
Main function . Parses options and calls helper function for any heavy lifting .
*/
int main ( int argc , char * argv [ ] )
{
int input_type = FILEDATA ;
int output_type = MIMETYPE ;
const char * mimetype ;
char * output = 0 ;
int i ;
hash_table_t launch_hash ;
2006-01-24 01:45:00 +10:00
locale_init ( ) ;
2005-09-20 23:26:39 +10:00
/*
Parse options
*/
while ( 1 )
{
2005-11-28 01:06:03 +10:00
# ifdef HAVE_GETOPT_LONG
2005-09-20 23:26:39 +10:00
static struct option
long_options [ ] =
{
{
" input-file-data " , no_argument , 0 , ' t '
}
,
{
" input-filename " , no_argument , 0 , ' f '
}
,
{
" input-mime " , no_argument , 0 , ' i '
}
,
{
" output-mime " , no_argument , 0 , ' m '
}
,
{
" output-description " , no_argument , 0 , ' d '
}
,
{
" output-action " , no_argument , 0 , ' a '
}
,
{
" help " , no_argument , 0 , ' h '
}
,
{
" version " , no_argument , 0 , ' v '
}
,
{
" launch " , no_argument , 0 , ' l '
}
,
{
0 , 0 , 0 , 0
}
}
;
int opt_index = 0 ;
int opt = getopt_long ( argc ,
argv ,
2006-05-27 00:55:51 +10:00
GETOPT_STRING ,
2005-09-20 23:26:39 +10:00
long_options ,
& opt_index ) ;
# else
int opt = getopt ( argc ,
argv ,
2006-05-27 00:55:51 +10:00
GETOPT_STRING ) ;
2005-09-20 23:26:39 +10:00
# endif
if ( opt = = - 1 )
break ;
switch ( opt )
{
case 0 :
break ;
case ' t ' :
input_type = FILEDATA ;
break ;
case ' f ' :
input_type = FILENAME ;
break ;
case ' i ' :
input_type = MIMETYPE ;
break ;
case ' m ' :
output_type = MIMETYPE ;
break ;
case ' d ' :
output_type = DESCRIPTION ;
break ;
case ' a ' :
output_type = ACTION ;
break ;
case ' l ' :
output_type = LAUNCH ;
break ;
case ' h ' :
print_help ( ) ;
exit ( 0 ) ;
case ' v ' :
2006-01-24 01:45:00 +10:00
printf ( _ ( " %s, version %s \n " ) , MIMEDB , PACKAGE_VERSION ) ;
2005-09-20 23:26:39 +10:00
exit ( 0 ) ;
case ' ? ' :
return 1 ;
}
}
if ( ( output_type = = LAUNCH ) & & ( input_type = = MIMETYPE ) )
{
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Can not launch a mimetype \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
print_help ( ) ;
exit ( 1 ) ;
}
if ( output_type = = LAUNCH )
hash_init ( & launch_hash , & hash_str_func , & hash_str_cmp ) ;
/*
Loop over all non option arguments and do the specified lookup
*/
//fprintf( stderr, "Input %d, output %d\n", input_type, output_type );
for ( i = optind ; ( i < argc ) & & ( ! error ) ; i + + )
{
/* Convert from filename to mimetype, if needed */
if ( input_type = = FILENAME )
{
mimetype = xdg_mime_get_mime_type_from_file_name ( argv [ i ] ) ;
}
else if ( input_type = = FILEDATA )
{
mimetype = xdg_mime_get_mime_type_for_file ( argv [ i ] ) ;
}
else
mimetype = xdg_mime_is_valid_mime_type ( argv [ i ] ) ? argv [ i ] : 0 ;
mimetype = xdg_mime_unalias_mime_type ( mimetype ) ;
if ( ! mimetype )
{
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Could not parse mimetype from argument '%s' \n " ) , MIMEDB , argv [ i ] ) ;
2005-09-20 23:26:39 +10:00
error = 1 ;
return 1 ;
}
/*
Convert from mimetype to whatever , if needed
*/
switch ( output_type )
{
case MIMETYPE :
{
output = ( char * ) mimetype ;
break ;
}
case DESCRIPTION :
{
output = get_description ( mimetype ) ;
break ;
}
case ACTION :
{
output = get_action ( mimetype ) ;
break ;
}
case LAUNCH :
{
/*
There may be more files using the same launcher , we
add them all up in little array_list_ts and launched
them together after all the arguments have been
parsed .
*/
array_list_t * l = ( array_list_t * ) hash_get ( & launch_hash , mimetype ) ;
output = 0 ;
if ( ! l )
{
l = my_malloc ( sizeof ( array_list_t ) ) ;
if ( l = = 0 )
{
break ;
}
al_init ( l ) ;
hash_put ( & launch_hash , mimetype , l ) ;
}
al_push ( l , argv [ i ] ) ;
}
}
/*
Print the glorious result
*/
if ( output )
{
printf ( " %s \n " , output ) ;
if ( output ! = mimetype )
free ( output ) ;
}
output = 0 ;
}
/*
Perform the actual launching
*/
2006-01-24 01:45:00 +10:00
if ( output_type = = LAUNCH & & ! error )
2005-09-20 23:26:39 +10:00
{
int i ;
array_list_t mimes ;
al_init ( & mimes ) ;
hash_get_keys ( & launch_hash , & mimes ) ;
for ( i = 0 ; i < al_get_count ( & mimes ) ; i + + )
{
char * mimetype = ( char * ) al_get ( & mimes , i ) ;
array_list_t * files = ( array_list_t * ) hash_get ( & launch_hash , mimetype ) ;
if ( ! files )
{
2006-01-24 01:45:00 +10:00
fprintf ( stderr , _ ( " %s: Unknown error \n " ) , MIMEDB ) ;
2005-09-20 23:26:39 +10:00
error = 1 ;
break ;
}
char * launcher = get_action ( mimetype ) ;
if ( launcher )
{
launch ( launcher , files , 0 ) ;
free ( launcher ) ;
}
}
hash_foreach ( & launch_hash , & clear_entry ) ;
hash_destroy ( & launch_hash ) ;
al_destroy ( & mimes ) ;
}
if ( launch_buff )
free ( launch_buff ) ;
2006-01-24 01:45:00 +10:00
if ( start_re )
{
regfree ( start_re ) ;
regfree ( stop_re ) ;
free ( start_re ) ;
free ( stop_re ) ;
}
2005-09-20 23:26:39 +10:00
xdg_mime_shutdown ( ) ;
return error ;
}