Click here to Skip to main content
Click here to Skip to main content

Full getopt Port for Unicode and Multibyte Microsoft Visual C, C++, or MFC Projects

By , 15 Oct 2012
 

Introduction

This software was written after hours of searching for a robust Microsoft C and C++ implementation of getopt, which led to devoid results.

This software is a modification of the Free Software Foundation, Inc. getopt library for parsing command line arguments and its purpose is to provide a Microsoft Visual C friendly derivative. The source code provides functionality for both Unicode and Multibyte builds and supports getopt, getopt_long, and getopt_long_only. Additionally the code supports the POSIXLY_CORRECT environment flag. The library uses the _UNICODE preprocessor directive when defining the getopt, getopt_long, and getopt_long_only functions. These functions are mapped back to getopt_a\getopt_w, getopt_long_a\getopt_long_w, and getopt_long_only_a\getopt_long_only_w respectively. This improvement was made to allow a single DLL to be used in both multibyte and Unicode projects.

The original GNU code used several header and implementation files containing numerous preprocessor directives specific to Linux environments which have been removed. After removing unneeded dependencies, it was condensed into a single header and implementation file which can be compiled into a DLL, LIB, or directly included into any Visual C, C++, or MFC project. The getopt library can be used by proprietary software, however; certain measures need to be taken to ensure proprietary code adheres to the Lesser GNU Public License for non-derivative works. Please refer to the licensing section of this article for more details on how this software is licensed.

For the sake of brevity, this article doesn't discuss how to use the getopt functions. Anyone new to using the getopt functions should refer to the GNU tutorial for using getopt.

Licensing

Since getopt is licensed under LGPL, it is free to use in proprietary software under some restrictions. When using this library as part of a proprietary software solution, it is important that the library is used as a dynamically linked library (DLL) and is not statically linked or directly compiled into proprietary source code. Static linkage requires that your software be released GPL. Therefore, by keeping the library separately referenced via Dynamic Link Library (DLL), allows the DLL to be modified and updated without changing the proprietary software which utilizes the library; under this condition proprietary software is said to "use" the library. Thus, it is not considered to be a derivative work and can be distributed freely under any license.

Preprocessor Definitions

Compiling getopt as a Dynamic Link Library (DLL) requires the preprocessor definition of EXPORTS_GETOPT. The definition of EXPORTS_GETOPT sets the internal preprocessor definition _GETOPT_API to the value __declspec(dllexport). Compiling getopt as a Static Library (LIB) or directly including the source and header file within a project requires the preprocessor definition of STATIC_GETOPT. The definition of STATIC_GETOPT clears the value of the internal preprocessor definition of _GETOPT_API. Compiling software to use getopt.dll requires that no library specific preprocessor definitions be used. When no library specific preprocessor definitions are used the value assigned to the internal preprocessor definition _GETOPT_API is __declspec(dllimport).

The code segment below demonstrates the logic outlined above:

#if defined(EXPORTS_GETOPT) && defined(STATIC_GETOPT)
    #error "The preprocessor definitions of EXPORTS_GETOPT 
        and STATIC_GETOPT can only be used individually"
#elif defined(STATIC_GETOPT)
#pragma message("Warning static builds of getopt violate the Lesser GNU Public License")
    #define _GETOPT_API
#elif defined(EXPORTS_GETOPT)
    #pragma message("Exporting getopt library")
    #define _GETOPT_API __declspec(dllexport)    
#else
    #pragma message("Importing getopt library")
    #define _GETOPT_API __declspec(dllimport)
#endif

The following code segment located in getopt.h is responsible for mapping the correct version of the getopt, getopt_long, and getopt_long_only functions. The getopt functions appended with _a to denote ANSI characters using the char type and Unicode functions are appended with _w to denote wide characters using the wchar_t type.

#ifdef _UNICODE
    #define getopt getopt_w
    #define getopt_long getopt_long_w
    #define getopt_long_only getopt_long_only_w
    #define option option_w
    #define optarg optarg_w
#else
    #define getopt getopt_a
    #define getopt_long getopt_long_a
    #define getopt_long_only getopt_long_only_a
    #define option option_a
    #define optarg optarg_a
#endif

Sample Code Provided

To help with understanding how to use the code, many versions have been provided for download. The following downloads are provided:

  • Visual Studio .NET 2010 ANSI Project
  • Visual Studio .NET 2008 ANSI Project
  • Visual Studio .NET 2008 MFC Project
  • Visual Studio .NET 2005 ANSI Project
  • Visual Studio .NET 2005 MFC Project
  • Visual Studio 6 ANSI Project
  • Visual Studio 6 MFC Project

Using the Code

The code is used identical to GNU getopt.

#include <stdio.h>
#include <stdlib.h>
#include "tchar.h"
#include "getopt.h"

int _tmain(int argc, TCHAR** argv)
{
    static int verbose_flag;
    int c;

    while (1)
    {        
        static struct option long_options[] =
        {
            {_T("verbose"), ARG_NONE, &verbose_flag, 1},
            {_T("brief"),   ARG_NONE, &verbose_flag, 0},
            {_T("add"),     ARG_NONE, 0, _T('a')},
            {_T("append"),  ARG_NONE, 0, _T('b')},
            {_T("delete"),  ARG_REQ,  0, _T('d')},
            {_T("create"),  ARG_REQ,  0, _T('c')},
            {_T("file"),    ARG_REQ, 0 , _T('f')},
            { ARG_NULL , ARG_NULL , ARG_NULL , ARG_NULL }
        };

        int option_index = 0;
        c = getopt_long(argc, argv, _T("abc:d:f:"), long_options, &option_index);

        // Check for end of operation or error
        if (c == -1)
            break;

        // Handle options
        switch (c)
        {
        case 0:
            /* If this option set a flag, do nothing else now. */
            if (long_options[option_index].flag != 0)
                break;
            _tprintf (_T("option %s"), long_options[option_index].name);
            if (optarg)
                _tprintf (_T(" with arg %s"), optarg);
            _tprintf (_T("\n"));
            break;

        case _T('a'):
            _tprintf(_T("option -a\n"));
            break;

        case _T('b'):
            _tprintf(_T("option -b\n"));
            break;

        case _T('c'):
            _tprintf (_T("option -c with value `%s'\n"), optarg);
            break;

        case _T('d'):
            _tprintf (_T("option -d with value `%s'\n"), optarg);
            break;

        case _T('f'):
            _tprintf (_T("option -f with value `%s'\n"), optarg);
            break;

        case '?':
            /* getopt_long already printed an error message. */
            break;

        default:
            abort();
        }
    }

    if (verbose_flag)
        _tprintf (_T("verbose flag is set\n"));


    if (optind < argc)
    {
        _tprintf (_T("non-option ARGV-elements: "));
        while (optind < argc) _tprintf (_T("%s "), argv[optind++]);
        _tprintf (_T("\n"));
    }
    return 0;
}     

Using this Code with C++ Precompiled Headers

When using this code statically within a C++ project with precompiled headers, it is necessary to rename getopt.c to getopt.cpp in order to circumvent the following compiler error:

"C1853 - Precompiled header file is from a previous version of the compiler, 
or the precompiled header is C++ and you are using it from C (or vice versa)."

Additionally precompiled header file must be added as the first include of the getopt.c or getopt.cpp file. For example, if you are using "stdafx.h" as the precompiled header, the following would be expected:

// File comments removed
#include "stdafx.h"
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include "getopt.h"

History

  • 02/03/2011 - Initial release
  • 02/20/2011 - Fixed L4 compiler warnings
  • 07/05/2011 - Added no_argument, required_argument, optional_argument def
  • 08/05/2011 - Fixed non-argument runtime bug which caused runtime exception
  • 08/09/2011 - Added code to export functions for DLL and LIB
  • 02/15/2012 - Fixed _GETOPT_THROW definition missing in implementation file
  • 08/03/2012 - Created separate functions for char and wchar_t characters so single DLL can do both Unicode and ANSI
  • 10/15/2012 - Modified to match latest GNU features

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)

About the Author

Ludvik Jerabek
Software Developer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 4memberlomo749 Jul '12 - 23:19 
Great work. But instead of defining TCHAR functions, I would have defined getoptA and getoptW, then
#ifdef _UNICODE
#define getopt getoptW
#else
#define getopt getoptA
#endif
This simplifies the creation of a getopt.dll which contains both (otherwise you have to create distinct getopt.dll and getopt_u.dll for ANSI and UNICODE).
Cheers - Lorenzo -
GeneralRe: My vote of 4memberLudvik Jerabek22 Jul '12 - 23:31 
Lorenzo,
 
Thanks for the information and kind words. I have been planning on going back and doing exactly what you outlined instead of having to compile separate dlls.
 
Regards,
 
Lu
GeneralRe: My vote of 4memberLudvik Jerabek2 Aug '12 - 8:33 
Just wanted to let you know I have updated my article and source they should have it posted any day now. The new source used defines in the header to toggle between the correct exported functions in the DLL rather than having to compile a unicode or an ANSI.
 
Kind Regards,
 
Lu
GeneralRe: My vote of 4memberlomo742 Aug '12 - 21:31 
I'm glad you accepted the suggestion.
Keep up the good work! - Lorenzo -
Question_GETOPT_THROW in getopt.cmemberazder55617 Nov '11 - 21:09 
Please add following changes in getopt.c for supress gcc error "declaration of <FUNC> has a different exception specifier"
 
#ifdef __cplusplus
#define _GETOPT_THROW throw()
#else
#define _GETOPT_THROW
#endif
 
...
 
int getopt (int argc, TCHAR *const *argv, const TCHAR *optstring) _GETOPT_THROW
 
...
 
int getopt_long (int argc, TCHAR *const *argv, const TCHAR *options, const struct option *long_options, int *opt_index) _GETOPT_THROW
 
...
 
int getopt_long_only (int argc, TCHAR *const *argv, const TCHAR *options, const struct option *long_options, int *opt_index) _GETOPT_THROW
...
AnswerRe: _GETOPT_THROW in getopt.cmemberLudvik Jerabek18 Nov '11 - 17:13 
Thanks! I am really not sure how I missed that Smile | :) as always feedback is appreciated!
Questionhow to USE itmemberericoporto24 Sep '11 - 11:39 
hello,
 
I'm new at developing in Windows. I want Visual Studio to use this getopt implementation.
 
I add the header in here: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include
 
Where do I add the binaries compiled by this project?
AnswerRe: how to USE itmemberLudvik Jerabek24 Sep '11 - 12:14 
Hello and thanks for using the library.
 
Since you are new to developing in windows, I will give you a quick run through a direct compile vs. using a DLL or LIB. This will keep things simple.
 

1. Copy the .c and .h file directly to your project directory where your other .c and .h files are located.
 
2. From Visual Studio add the getopt.h and getopt.c to your project directly. If you are using MFC see the section of the article on using the files in MFC projects with precompiled headers.
 
3. Add the STATIC_GETOPT preprocessor definition to your project by doing the following:
1. From Visual Studio Solution Explorer, Right Click "Your Project Name" select properties.
2. Under The "Your Project Name" Properties Page, From the left hand tree control Click on C/C++ --> Preprocessor.
3. Edit the Preprocessor Definitions value located to the left of the tree control. Goto the end of line .. you should see something like WIN32;_DEBUG;... add STATIC_GETOPT to the end of that line. It should look something like this WIN32;_DEGUG;STATIC_GETOPT
 
4. Add #include "getopt.h" to the file you wish to perform command line parsing in.
 
I hope this helps, If not feel free to contact me again and I am willing to chat by phone to help you out. I could send you email's with screen shots if you need.
 
You should be able to place the files in your project folder without including them in your project. You can also add additional include paths to your project under the project properties. I would not advise placing the files into the default VC library path. The most important thing you need to do is define STATIC_GETOPT. I would advise reading a tutorial on creating and using DLLs in Visual C projects.

 
Regards,
 
Lu
QuestionJust an advisory about the licencemembersgllama9 Aug '11 - 5:59 
> it was condensed into a single header and implementation file
> which could be added to any Visual C, C++, or MFC project.
> ...
> Since getopt is licensed under LGPL, it is free to use in proprietary software
 
To use LGPL'ed code in closed source software you have to put the LGPL'ed code into a DLL, rather than just copy the header and implementation file willy-nilly into your project, and you add a note telling the User that they can find the source code for the DLL at some URL. The end result is that they have the possibility of re-building the DLL, along with any tweaks or fixes they want, and then use that with your program.
AnswerRe: Just an advisory about the licencememberLudvik Jerabek9 Aug '11 - 12:31 
I have submitted a new article and code to Code Project to address the issue. The new article and projects utilize DLLs and covers GPL licensing and usage for proprietary works.

modified on Wednesday, September 21, 2011 9:11 AM

GeneralMy vote of 5memberMicroImaging8 Aug '11 - 9:02 
Thank you for this original and well thought out article especially your timely response for bug fixes.
GeneralRe: My vote of 5memberseachanged26 Dec '11 - 12:01 
I vote 5 also.
I'm using it as-is.
I appreciate the multiple microsoft compiler support, and
I especially appreciate how easy you've made it to do the
"right thing" with regard to the Lesser GPL license.
GeneralMy vote of 1memberkouhinn26 Jul '11 - 20:42 
"getopt_mb_uni_mfc_vc8.exe --help"cause crash !!!!
GeneralRe: My vote of 1memberLudvik Jerabek3 Aug '11 - 15:25 
Thanks, the bug has been identified and fixed.
 
The new code should be posted in a couple of days. Turns out the issue was only occurring in the unicode build of the project when in debug mode.
 
Thanks again for your feedback it is appreciated.
GeneralThanks & suggestionmemberLu.P11 Mar '11 - 11:27 
Hello,
 
thank you for the nice piece of code!
 
I'd have two suggestions:
 
1) I'm aware of name collisions of 'ARG_*" #defined names, so wouldn't be better to a separate them e.g. to a struct like (a couple of implementations are possible):
 
struct GETOPT_ARG
{ enum
  { NONE = 0, // To force use like 'GETOPT_ARG::NONE' in 'struct option'
    ...
 
2) Would it be possible to provide an optional error handler to dispatch situations like _ftprintf(stderr, _T("%s: unrecognized option '--%s'\n", ...))?
 
I'm not sure how best to provide this in C (maybe by a function set_getopt_error_handler(..., void * user_par) which would replace a default handler (being implemented for this).
 
Or - if getopt() was ported to C++, the error handler would be provided by virtual function like virtual void on_error(TCHAR * msg, va_list args).
 
Anyway, your code "as is" is very handy and useful; thanks again.
 
Lukas
GeneralRe: Thanks & suggestionmemberLudvik Jerabek12 Mar '11 - 14:53 
I agree with you; however, to keep the code as compatible with the original GNU version no major implementation details were modified. I have implemented a version which uses an enum as well Smile | :) for the exact reason you stated. Likewise keeping the code portable across C and C++ was also the goal. Thanks again for your advice and feedback. The using a function pointer would be the best way to override the error handler and may be something I implement as a nonstandard GNU getopt feature.
 
Regards,
 
Lu
GeneralRe: Thanks & suggestionmemberLu.P13 Mar '11 - 6:16 
Thanks for your reaction.
 
So it seems that some non-standard (GNU non-compatible?) features might be conditioned by a special macro #definition; and this might affect ARG_NONE etc. symbols as well as an optional error handler.
 
Nice to hear you would implement this (or - even - you already have!) - having such "extras" might save time of everyone who thinks about similar alterations; so this might keep your code "elastic" enough and avoid cloning it into many "almost similar" derivations.
 
Best regards,
 
LuP
GeneralMy vote of 5memberMember 16579821 Feb '11 - 20:48 
Thank You! Very nice! I was too lazy to do that!
GeneralVery nice!mentorHans Dietrich19 Feb '11 - 8:09 
getopt is one of my favorites. Thumbs Up | :thumbsup:
 
Suggestion: It would be nice to compile at warning level 4 without warnings.
Best wishes,
Hans
 

[Hans Dietrich Software]

GeneralRe: Very nice!memberLudvik Jerabek19 Feb '11 - 13:23 
Hans,
 
Thanks for you input. I will try to take a look at the compiler warnings when compiling at level 4.
 
Regards,
 
Lu
GeneralRe: Very nice!memberLudvik Jerabek20 Feb '11 - 11:52 
Hans,
 
I have fixed the L4 warnings. Depending on which MS compiler you are using it my give a single warning about deprecation.
 
Regards,
 
Ludvik
GeneralMy vote of 5memberSharjith14 Feb '11 - 3:59 
Good work! Many UNIX/Linux features are missing in Microsoft SDK. This is one of them that's really useful to make portable applications. Thanks..
RegardsSmile | :)
N. Sharjith

GeneralRe: My vote of 5memberLudvik Jerabek14 Feb '11 - 5:07 
Thanks, that is exactly why I ported it.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 15 Oct 2012
Article Copyright 2011 by Ludvik Jerabek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid