OSDN Git Service

A big batch of patches from eddyg.
authorjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 6 Jun 2007 02:09:41 +0000 (02:09 +0000)
committerjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 6 Jun 2007 02:09:41 +0000 (02:09 +0000)
Adds following options to the CLI:

--subtitle-scan for auto-selecting subtitles for foreign language segments in native language audio tracks
--native-language for auto-selecting subtitles for foreign language audio without knowing the track number
--longest for auto-selecting the longest title like in the MacGui
--turbo for improving speed on the first pass of a two pass encode with x264 (based on superdump's turbo x264 options in mencoder).

Thanks, eddyg!

git-svn-id: svn://localhost/HandBrake/trunk@600 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.h
libhb/hb.c
libhb/reader.c
libhb/work.c
test/test.c

index 8b09c85..116f99a 100644 (file)
@@ -245,6 +245,10 @@ struct hb_job_s
     int             mux;
     const char          * file;
 
+    int subtitle_scan;
+    hb_subtitle_t ** select_subtitle;
+    char * native_language;
+
 #ifdef __LIBHB__
     /* Internal data */
     hb_handle_t   * h;
@@ -360,6 +364,8 @@ struct hb_subtitle_s
     char lang[1024];
     char iso639_2[4];
 
+    int hits;     /* How many hits/occurrences of this subtitle */
+
 #ifdef __LIBHB__
     /* Internal data */
     hb_fifo_t * fifo_in;  /* SPU ES */
index 467ba92..0550c3c 100644 (file)
@@ -546,13 +546,87 @@ void hb_add( hb_handle_t * h, hb_job_t * job )
         }
     }
 
-    /* Copy the subtitle we want (or not) */
     title_copy->list_subtitle = hb_list_init();
-    if( ( subtitle = hb_list_item( title->list_subtitle, job->subtitle ) ) )
+
+    if( job->pass != 1 ) 
     {
-        subtitle_copy = malloc( sizeof( hb_subtitle_t ) );
-        memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) );
-        hb_list_add( title_copy->list_subtitle, subtitle_copy );
+        /* Copy the subtitle we want (or not) 
+         */
+        if( ( subtitle = hb_list_item( title->list_subtitle, job->subtitle ) ) )
+        {
+            subtitle_copy = malloc( sizeof( hb_subtitle_t ) );
+            memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) );
+            hb_list_add( title_copy->list_subtitle, subtitle_copy );
+        }
+    } else { 
+        char audio_lang[4];
+
+        memset( audio_lang, 0, sizeof( audio_lang ) );
+
+        /*
+         * Pass 1, do we want to do a subtitle scan?
+         */
+        if( job->subtitle_scan ) 
+        {
+            /*
+             * Search for all occurances of the audio language in the
+             * subtitles and add them all to the
+             * title_copy->list_subtitle. First of all find the
+             * language for the audio.
+             */
+            for( i = 0; i < 8; i++ )
+            {
+                if( job->audios[i] < 0 )
+                {
+                    break;
+                }
+                if( ( audio = hb_list_item( title->list_audio, job->audios[i] ) ) )
+                {
+                    strncpy(audio_lang, audio->iso639_2, sizeof(audio_lang));
+                }
+            }
+        }
+
+        /*
+         * If we have a native language now is the time to see whether we want
+         * to enable subtitles for it if it differs from the audio language.
+         */
+        if( job->native_language ) 
+        {
+            if( strncasecmp( job->native_language, audio_lang, 
+                             sizeof( audio_lang ) ) != 0 )
+            {             
+
+                hb_log( "Enabled subtitles in native language '%s', audio is in '%s'",
+                        job->native_language, audio_lang);
+                /*
+                 * The main audio track is not in our native language, so switch
+                 * the subtitle scan to use our native language instead.
+                 */
+                strncpy( audio_lang, job->native_language, sizeof( audio_lang ) );
+            } else {
+                /*
+                 * native language is irrelevent, free it.
+                 */
+                free( job->native_language );
+                job->native_language = NULL;
+            }
+        }
+
+        for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) 
+        {
+            subtitle = hb_list_item( title->list_subtitle, i );
+            if( strcmp( subtitle->iso639_2, audio_lang ) == 0 ) 
+            {
+                /*
+                 * Matched subtitle language with audio language, so
+                 * add this to our list to scan.
+                 */
+                subtitle_copy = malloc( sizeof( hb_subtitle_t ) );
+                memcpy( subtitle_copy, subtitle, sizeof( hb_subtitle_t ) );
+                hb_list_add( title_copy->list_subtitle, subtitle_copy );
+            }
+        }
     }
 
     /* Copy the job */
index 35939f9..4767f87 100644 (file)
@@ -138,12 +138,26 @@ static hb_fifo_t * GetFifoForId( hb_job_t * job, int id )
         return job->fifo_mpeg2;
     }
 
-    if( ( subtitle = hb_list_item( title->list_subtitle, 0 ) ) &&
-        id == subtitle->id )
-    {
-        return subtitle->fifo_in;
+    if (job->subtitle_scan) {
+        /*
+         * Count the occurances of the subtitles, don't actually return any to encode.
+         */
+        for (i=0; i < hb_list_count(title->list_subtitle); i++) {
+            subtitle =  hb_list_item( title->list_subtitle, i);
+            if (id == subtitle->id) {
+                /*
+                 * A hit, count it.
+                 */
+                subtitle->hits++;
+            }
+        }
+    } else {
+        if( ( subtitle = hb_list_item( title->list_subtitle, 0 ) ) &&
+            id == subtitle->id )
+        {
+            return subtitle->fifo_in;
+        }
     }
-
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
     {
         audio = hb_list_item( title->list_audio, i );
index aa77097..b7d02cd 100644 (file)
@@ -100,6 +100,11 @@ static void do_job( hb_job_t * job, int cpu_count )
     hb_audio_t   * audio;
     hb_subtitle_t * subtitle;
     int done;
+    unsigned int subtitle_highest = 0;
+    unsigned int subtitle_highest_id = 0;
+    unsigned int subtitle_lowest = -1;
+    unsigned int subtitle_lowest_id = 0;
+    unsigned int subtitle_hit = 0;
 
     title = job->title;
 
@@ -191,17 +196,31 @@ static void do_job( hb_job_t * job, int cpu_count )
     w->config   = &job->config;
     hb_list_add( job->list_work, w );
 
-    subtitle = hb_list_item( title->list_subtitle, 0 );
-    if( subtitle )
+    if( job->select_subtitle && !job->subtitle_scan ) 
     {
-        hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang );
+        hb_list_add( title->list_subtitle, *( job->select_subtitle ) );
+    }
 
-        subtitle->fifo_in  = hb_fifo_init( 8 );
-        subtitle->fifo_raw = hb_fifo_init( 8 );
+    for( i=0; i < hb_list_count(title->list_subtitle); i++ ) 
+    {
+        subtitle =  hb_list_item( title->list_subtitle, i );
 
-        hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) );
-        w->fifo_in  = subtitle->fifo_in;
-        w->fifo_out = subtitle->fifo_raw;
+        if( subtitle )
+        {
+            hb_log( " + subtitle %x, %s", subtitle->id, subtitle->lang );
+            
+            subtitle->fifo_in  = hb_fifo_init( 8 );
+            subtitle->fifo_raw = hb_fifo_init( 8 );
+            
+            if (!job->subtitle_scan) {
+                /*
+                 * Don't add threads for subtitles when we are scanning
+                 */
+                hb_list_add( job->list_work, ( w = getWork( WORK_DECSUB ) ) );
+                w->fifo_in  = subtitle->fifo_in;
+                w->fifo_out = subtitle->fifo_raw;
+            }
+        }
     }
 
     if( job->acodec & HB_ACODEC_AC3 )
@@ -499,10 +518,13 @@ static void do_job( hb_job_t * job, int cpu_count )
     hb_fifo_close( &job->fifo_sync );
     hb_fifo_close( &job->fifo_render );
     hb_fifo_close( &job->fifo_mpeg4 );
-    if( subtitle )
-    {
-        hb_fifo_close( &subtitle->fifo_in );
-        hb_fifo_close( &subtitle->fifo_raw );
+    for (i=0; i < hb_list_count(title->list_subtitle); i++) {
+        subtitle =  hb_list_item( title->list_subtitle, i);
+        if( subtitle )
+        {
+            hb_fifo_close( &subtitle->fifo_in );
+            hb_fifo_close( &subtitle->fifo_raw );
+        }
     }
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
     {
@@ -513,6 +535,79 @@ static void do_job( hb_job_t * job, int cpu_count )
         hb_fifo_close( &audio->fifo_out );
     }
     
+    /*
+     * Before closing the title print out our subtitle stats if we need to
+     * Find the highest and lowest.
+     */
+    for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) 
+    {
+        subtitle =  hb_list_item( title->list_subtitle, i );
+        hb_log( "Subtitle stream 0x%x '%s': %d hits",
+               subtitle->id, subtitle->lang, subtitle->hits );
+        if( subtitle->hits > subtitle_highest ) 
+        {
+            subtitle_highest = subtitle->hits;
+            subtitle_highest_id = subtitle->id;
+        } 
+
+        if( subtitle->hits < subtitle_lowest ) 
+        {
+            subtitle_lowest = subtitle->hits;
+            subtitle_lowest_id = subtitle->id;
+        }
+    }
+
+    if( job->native_language ) {
+        /*
+         * We still have a native_language, so the audio and subtitles are
+         * different, so in this case it is a foreign film and we want to
+         * select the first subtitle in our language.
+         */
+        subtitle =  hb_list_item( title->list_subtitle, 0 );
+        subtitle_hit = subtitle->id;
+        hb_log( "Found a native-language subtitle id 0x%x", subtitle_hit);
+    } else {
+        if( subtitle_lowest < subtitle_highest ) 
+        {
+            /*
+             * OK we have more than one, and the lowest is lower, but how much
+             * lower to qualify for turning it on by default?
+             *
+             * Let's say 20% as a default.
+             */
+            if( subtitle_lowest < ( subtitle_highest * 0.2 ) ) 
+            {
+                subtitle_hit = subtitle_lowest_id;
+                hb_log( "Found a subtitle candidate id 0x%x",
+                        subtitle_hit );
+            } else {
+                hb_log( "No candidate subtitle detected during subtitle-scan");
+            }
+        }
+    }
+
+    if( job->select_subtitle ) 
+    {
+        if( job->subtitle_scan ) 
+        {
+            for( i=0; i < hb_list_count( title->list_subtitle ); i++ ) 
+            {
+                subtitle =  hb_list_item( title->list_subtitle, i );
+                if( subtitle->id = subtitle_hit ) 
+                {
+                    hb_list_rem( title->list_subtitle, subtitle );
+                    *( job->select_subtitle ) = subtitle;
+                }
+            }
+        } else {
+            /*
+             * Must be the second pass - we don't need this anymore.
+             */
+            free( job->select_subtitle );
+            job->select_subtitle = NULL;
+        }
+    }
+
     hb_title_close( &job->title );
     free( job );
 }
index 3ed30e4..addee82 100644 (file)
@@ -20,6 +20,9 @@ static char * input       = NULL;
 static char * output      = NULL;
 static char * format      = NULL;
 static int    titleindex  = 1;
+static int    longest_title = 0;
+static int    subtitle_scan = 0;
+static char * native_language = NULL;
 static int    twoPass     = 0;
 static int    deinterlace = 0;
 static int    grayscale   = 0;
@@ -51,6 +54,8 @@ static char     *x264opts             = NULL;
 static char      *x264opts2    = NULL;
 static int       maxHeight             = 0;
 static int       maxWidth              = 0;
+static int    turbo_opts_enabled = 0;
+static char * turbo_opts = "no-fast-pskip=0:subme=1:me=dia:trellis=0:analyse=none";
 
 /* Exit cleanly on Ctrl-C */
 static volatile int die = 0;
@@ -116,6 +121,13 @@ int main( int argc, char ** argv )
 
     /* Feed libhb with a DVD to scan */
     fprintf( stderr, "Opening %s...\n", input );
+
+    if (longest_title) {
+        /*
+         * We need to scan for all the titles in order to find the longest
+         */
+        titleindex = 0;
+    }
     hb_scan( h, input, titleindex );
 
     /* Wait... */
@@ -181,6 +193,7 @@ int main( int argc, char ** argv )
     if( output ) free( output );
     if( format ) free( format );
     if( audios ) free( audios );
+    if (native_language ) free (native_language );
        if( x264opts ) free (x264opts );
        if( x264opts2 ) free (x264opts2 );
        
@@ -245,7 +258,8 @@ static void PrintTitleInfo( hb_title_t * title )
     for( i = 0; i < hb_list_count( title->list_subtitle ); i++ )
     {
         subtitle = hb_list_item( title->list_subtitle, i );
-        fprintf( stderr, "    + %d, %s\n", i + 1, subtitle->lang );
+        fprintf( stderr, "    + %d, %s (iso639-2: %s)\n", i + 1, subtitle->lang,
+            subtitle->iso639_2);
     }
 }
 
@@ -284,6 +298,44 @@ static int HandleEvents( hb_handle_t * h )
                 die = 1;
                 break;
             }
+           if( longest_title )
+           {
+                int i;
+                int longest_title_idx=0;
+                int longest_title_pos=-1;
+                int longest_title_time=0;
+                int title_time;
+                
+                fprintf( stderr, "Searching for longest title...\n" );
+
+                for( i = 0; i < hb_list_count( list ); i++ )
+                {
+                    title = hb_list_item( list, i );
+                    title_time = (title->hours*60*60 ) + (title->minutes *60) + (title->seconds);
+                    fprintf( stderr, " + Title (%d) index %d has length %dsec\n", 
+                             i, title->index, title_time );
+                    if( longest_title_time < title_time )
+                    {
+                        longest_title_time = title_time;
+                        longest_title_pos = i;
+                        longest_title_idx = title->index;
+                    }              
+                }
+                if( longest_title_pos == -1 ) 
+                {
+                    fprintf( stderr, "No longest title found.\n" );
+                    die = 1;
+                    break;
+                }
+                titleindex = longest_title_idx;
+                fprintf( stderr, "Found longest title, setting title to %d\n", 
+                         longest_title_idx);
+
+                title = hb_list_item( list, longest_title_pos);
+            } else {
+                title = hb_list_item( list, 0 );
+            }
+
             if( !titleindex )
             {
                 /* Scan-only mode, print infos and exit */
@@ -298,7 +350,6 @@ static int HandleEvents( hb_handle_t * h )
             }
 
             /* Set job settings */
-            title = hb_list_item( list, 0 );
             job   = title->job;
 
             PrintTitleInfo( title );
@@ -479,6 +530,11 @@ static int HandleEvents( hb_handle_t * h )
                 job->subtitle = sub - 1;
             }
 
+            if( native_language )
+            {
+                job->native_language = strdup( native_language );
+            }
+
             if( job->mux )
             {
                 job->mux = mux;
@@ -490,31 +546,95 @@ static int HandleEvents( hb_handle_t * h )
                 job->crf = 1;
             }
 
-            if (x264opts != NULL && *x264opts != '\0' )
+            ifx264opts != NULL && *x264opts != '\0' )
             {
                 fprintf( stderr, "Applying the following x264 options: %s\n", 
-                        x264opts);
+                         x264opts);
                 job->x264opts = x264opts;
             }
             else /*avoids a bus error crash when options aren't specified*/
-                       {
-                               job->x264opts =  NULL;
-                       }
-                       if (maxWidth)
-                               job->maxWidth = maxWidth;
-                       if (maxHeight)
-                               job->maxHeight = maxHeight;
+            {
+                job->x264opts =  NULL;
+            }
+            if (maxWidth)
+                job->maxWidth = maxWidth;
+            if (maxHeight)
+                job->maxHeight = maxHeight;
                                
             if( twoPass )
             {
+                /*
+                 * If subtitle_scan is enabled then only turn it on
+                 * for the first pass and then off again for the
+                 * second. 
+                 */
                 job->pass = 1;
+                job->subtitle_scan = subtitle_scan;
+                if( subtitle_scan ) 
+                {
+                    fprintf( stderr, "Subtitle Scan Enabled - enabling "
+                             "subtitles if found for foreign language segments\n");
+                    job->select_subtitle = malloc(sizeof(hb_subtitle_t*));
+                    *(job->select_subtitle) = NULL;
+                } 
+
+                /*
+                 * If turbo options have been selected then append them
+                 * to the x264opts now (size includes one ':' and the '\0')
+                 */
+                if( turbo_opts_enabled ) 
+                {
+                    int size = strlen(x264opts) + strlen(turbo_opts) + 2;
+                    char *tmp_x264opts;
+                        
+                    tmp_x264opts = malloc(size * sizeof(char));
+                    if( x264opts ) 
+                    {
+                        snprintf( tmp_x264opts, size, "%s:%s", 
+                                  x264opts, turbo_opts );  
+                        free( x264opts );
+                    } else {
+                        /*
+                         * No x264opts to modify, but apply the turbo options
+                         * anyway as they may be modifying defaults
+                         */
+                        snprintf( tmp_x264opts, size, "%s", 
+                                  turbo_opts );
+                    }
+                    x264opts = tmp_x264opts;
+
+                    fprintf( stderr, "Modified x264 options for pass 1 to append turbo options: %s\n",
+                             x264opts );
+
+                    job->x264opts = x264opts;
+                }     
                 hb_add( h, job );
                 job->pass = 2;
-                               job->x264opts = x264opts2;
+                /*
+                 * On the second pass we turn off subtitle scan so that we
+                 * can actually encode using any subtitles that were auto
+                 * selected in the first pass (using the whacky select-subtitle
+                 * attribute of the job).
+                 */
+                job->subtitle_scan = 0;
+
+                job->x264opts = x264opts2;
+                
                 hb_add( h, job );
             }
             else
             {
+                /*
+                 * Turn on subtitle scan if requested, note that this option
+                 * precludes encoding of any actual subtitles.
+                 */
+                job->subtitle_scan = subtitle_scan;
+                if ( subtitle_scan ) 
+                {
+                    fprintf( stderr, "Subtitle Scan Enabled, will scan all "
+                             "subtitles matching the audio language for any\n"
+                             "that meet our auto-selection criteria.\n");
+                }
                 job->pass = 0;
                 hb_add( h, job );
             }
@@ -606,6 +726,7 @@ static void ShowHelp()
        "    -i, --input <string>    Set input device\n"
        "    -t, --title <number>    Select a title to encode (0 to scan only,\n"
     "                            default: 1)\n"
+    "    -L, --longest           Select the longest title\n"
     "    -c, --chapters <string> Select chapters (e.g. \"1-3\" for chapters\n"
     "                            1 to 3, or \"3\" for chapter 3 only,\n"
     "                            default: all chapters)\n"
@@ -624,6 +745,13 @@ static void ShowHelp()
        "    -Y, --maxHeight <#>     Set maximum height\n"
        "    -X, --maxWidth <#>      Set maximum width\n"
        "    -s, --subtitle <number> Select subtitle (default: none)\n"
+    "    -U, --subtitle-scan     Scan for subtitles on the first pass, and choose\n"
+    "                            the one that's only used 20 percent of the time\n"
+    "                            or less. This should locate subtitles for short\n"
+    "                            foreign language segments. Only works with 2-pass.\n"
+    "    -N, --native-language   Select subtitles with this language if it does not\n"
+    "          <string>          match the Audio language. Provide the language's\n"
+    "                            iso639-2 code (fre, eng, spa, dut, et cetera)\n"
        "    -m, --markers           Add chapter markers (mp4 output format only)\n"
        "\n"
        
@@ -673,10 +801,14 @@ static void ShowHelp()
        "\n"
        
        
-       "### Advanced H264 Options----------------------------------------------------\n\n"
-       "    -x, --x264opts <string> Specify advanced x264 options in the\n"
-       "                            same style as mencoder:\n"
-       "                            option1=value1:option2=value2\n" );
+    "### Advanced H264 Options----------------------------------------------------\n\n"
+    "    -x, --x264opts <string> Specify advanced x264 options in the\n"
+    "                            same style as mencoder:\n"
+    "                            option1=value1:option2=value2\n"
+    "    -T, --turbo             When using 2-pass use the turbo options\n"
+    "                            on the first pass to improve speed\n"
+    "                            (only works with x264, affects PSNR by about 0.05dB,\n"
+    "                            and increases first pass speed two to four times)\n");
 }
 
 /****************************************************************************
@@ -698,11 +830,14 @@ static int ParseOptions( int argc, char ** argv )
             { "output",      required_argument, NULL,    'o' },
 
             { "title",       required_argument, NULL,    't' },
+            { "longest",     no_argument,       NULL,    'L' },
             { "chapters",    required_argument, NULL,    'c' },
             { "markers",     optional_argument, NULL,    'm' },
             { "audio",       required_argument, NULL,    'a' },
             { "mixdown",     required_argument, NULL,    '6' },
             { "subtitle",    required_argument, NULL,    's' },
+            { "subtitle-scan", no_argument,     NULL,    'U' },
+            { "native-language", required_argument, NULL,'N' },
 
             { "encoder",     required_argument, NULL,    'e' },
             { "aencoder",    required_argument, NULL,    'E' },
@@ -720,10 +855,12 @@ static int ParseOptions( int argc, char ** argv )
             { "ab",          required_argument, NULL,    'B' },
             { "rate",        required_argument, NULL,    'r' },
             { "arate",       required_argument, NULL,    'R' },
-                       { "crf",                 no_argument,           NULL,    'Q' },
-                       { "x264opts",    required_argument, NULL,    'x' },
-                       { "maxHeight",   required_argument, NULL,        'Y' },
-                       { "maxWidth",    required_argument, NULL,        'X' },
+            { "crf",         no_argument,       NULL,    'Q' },
+            { "x264opts",    required_argument, NULL,    'x' },
+            { "turbo",       no_argument,       NULL,    'T' },
+            
+            { "maxHeight",   required_argument, NULL,    'Y' },
+            { "maxWidth",    required_argument, NULL,    'X' },
                        
             { 0, 0, 0, 0 }
           };
@@ -732,7 +869,7 @@ static int ParseOptions( int argc, char ** argv )
         int c;
 
         c = getopt_long( argc, argv,
-                         "hvuC:f:i:o:t:c:ma:6:s:e:E:2dgpw:l:n:b:q:S:B:r:R:Qx:Y:X:",
+                         "hvuC:f:i:o:t:Lc:ma:6:s:UN:e:E:2dgpw:l:n:b:q:S:B:r:R:Qx:TY:X:",
                          long_options, &option_index );
         if( c < 0 )
         {
@@ -767,6 +904,9 @@ static int ParseOptions( int argc, char ** argv )
             case 't':
                 titleindex = atoi( optarg );
                 break;
+            case 'L':
+                longest_title = 1;
+                break;
             case 'c':
             {
                 int start, end;
@@ -823,7 +963,12 @@ static int ParseOptions( int argc, char ** argv )
             case 's':
                 sub = atoi( optarg );
                 break;
-
+            case 'U':
+                subtitle_scan = 1;
+                break;
+            case 'N':
+                native_language = strdup( optarg );
+                break;
             case '2':
                 twoPass = 1;
                 break;
@@ -942,19 +1087,22 @@ static int ParseOptions( int argc, char ** argv )
             case 'B':
                 abitrate = atoi( optarg );
                 break;
-                       case 'Q':
-                               crf = 1;
-                               break;
-                       case 'x':
-                               x264opts = strdup( optarg );
-                               x264opts2 = strdup( optarg );
-                           break;
-                       case 'Y':
-                               maxHeight = atoi( optarg );
-                               break;
-                       case 'X':
-                               maxWidth = atoi (optarg );
-                               break;
+            case 'Q':
+                crf = 1;
+                break;
+            case 'x':
+                x264opts = strdup( optarg );
+                x264opts2 = strdup( optarg );
+                break;
+            case 'T':
+                turbo_opts_enabled = 1;
+                break;
+            case 'Y':
+                maxHeight = atoi( optarg );
+                break;
+            case 'X':
+                maxWidth = atoi (optarg );
+                break;
                                
             default:
                 fprintf( stderr, "unknown option (%s)\n", argv[optind] );