Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a portable command line wrapper in C

I'm writing a perl module called perl5i. Its aim is to fix a swath of common Perl problems in one module (using lots of other modules).

To invoke it on the command line for one liners you'd write: perl -Mperl5i -e 'say "Hello"' I think that's too wordy so I'd like to supply a perl5i wrapper so you can write perl5i -e 'say "Hello"'. I'd also like people to be able to write scripts with #!/usr/bin/perl5i so it must be a compiled C program.

I figured all I had to do was push "-Mperl5i" onto the front of the argument list and call perl. And that's what I tried.

#include <unistd.h>
#include <stdlib.h>

/*
 * Meant to mimic the shell command
 *     exec perl -Mperl5i "$@"
 *
 * This is a C program so it works in a #! line.
 */

int main (int argc, char* argv[]) {
    int i;
    /* This value is set by a program which generates this C file */
    const char* perl_cmd = "/usr/local/perl/5.10.0/bin/perl";
    char* perl_args[argc+1];
    perl_args[0] = argv[0];
    perl_args[1] = "-Mperl5i";

    for( i = 1;  i <= argc;  i++ ) {
        perl_args[i+1] = argv[i];
   }

   return execv( perl_cmd, perl_args );
}

Windows complicates this approach. Apparently programs in Windows are not passed an array of arguments, they are passed all the arguments as a single string and then do their own parsing! Thus something like perl5i -e "say 'Hello'" becomes perl -Mperl5i -e say 'Hello' and Windows can't deal with the lack of quoting.

So, how can I handle this? Wrap everything in quotes and escapes on Windows? Is there a library to handle this for me? Is there a better approach? Could I just not generate a C program on Windows and write it as a perl wrapper as it doesn't support #! anyway?

UPDATE: Do be more clear, this is shipped software so solutions that require using a certain shell or tweaking the shell configuration (for example, alias perl5i='perl -Mperl5i') aren't satisfactory.

like image 230
Schwern Avatar asked Jul 07 '09 23:07

Schwern


2 Answers

For Windows, use a batch file.

perl5i.bat

@echo off
perl -Mperl5i %*

%* is all the command line parameters minus %0.

On Unixy systems, a similar shell script will suffice.

Update:

I think this will work, but I'm no shell wizard and I don't have an *nix system handy to test.

perl5i

#!bash

perl -Mperl5i $@

Update Again:

DUH! Now I understood your #! comment correctly. My shell script will work from the CLI but not in a #! line, since #!foo requries that foo is a binary file.

Disregard previous update.

It seems like Windows complicates everything. I think your best there is to use a batch file.

You could use a file association, associate .p5i with perl -Mperl5i %*. Of course this means mucking about in the registry, which is best avoided IMO. Better to include instructions on how to manually add the association in your docs.

Yet another update

You might want to look at how parl does it.

like image 86
daotoad Avatar answered Oct 22 '22 15:10

daotoad


I can't reproduce the behaviour your describe:

/* main.c */

#include <stdio.h>

int main(int argc, char *argv[]) {
    int i;
    for (i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }
    return 0;
}

C:\> ShellCmd.exe a b c
ShellCmd.exe
a
b
c

That's with Visual Studio 2005.

like image 39
hexten Avatar answered Oct 22 '22 16:10

hexten