/*
 * wordcnv: use the Microsoft Office converters for command-line conversion
 *
 * Copyright (C) 2002 Sean Young <sean@mess.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * 
 * Since it relies on closed-source commercial libraries (the converter
 * dll's) it cannot be released under GPL. Therefore it is LPGL.
 *
 * It only requires kernel32.lib and should compile under winelib (note the
 * #ifdef below).
 */

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "convapi.h"
#ifdef WINELIB
#define stricmp         strcasecmp
#endif

typedef struct converter {
   HINSTANCE            handle;
   InitConverter32      *InitConverter;
   UninitConverter      *FreeConverter;
   RegisterApp          *RegisterApp;
   ForeignToRtf32       *ForeignToRtf;
   RtfToForeign32       *RtfToForeign;
   IsFormatCorrect32    *IsFormatCorrect;
   CchFetchLpszError    *FetchError;
   GetReadNames         *GetRead;
   GetWriteNames        *GetWrite;
   char                 filename[MAX_PATH];
} converter; 


typedef struct ms_converter {
    char    filename[MAX_PATH];
    char    class_name[MAX_PATH];
    char    description[MAX_PATH];
    char    extensions[MAX_PATH];
    int     type;
    struct  ms_converter *next;
} ms_converter;

enum ms_conv_type {
    MS_OTHER_TO_RTF,
    MS_RTF_TO_OTHER,
    MS_LAST 
};

static struct {
    int  list;
    int  help;
    int  identify;
    int  files;
    char import_class[MAX_PATH], export_class[MAX_PATH];
    char input_file[MAX_PATH], output_file[MAX_PATH];
} options;

static char *wordcnv_dir = NULL, cnv_dir[MAX_PATH];
static int  verbose = 0;
static HANDLE fin, fout;
static HGLOBAL  hin, hout;
static char *progname;

static struct ms_converter *msconverters = NULL;

static void strcpy_fix_double_null (char *des, const char *src)
{
    char last = 1;

    do {
        last = *src;
        *des++ = *src++;
    } while (last || *src);

    *des = 0; 
}

static void print_cnv_error (int err, struct converter *cnv)
{
    char buf[512];

    if (cnv->FetchError && cnv->FetchError (err, buf, sizeof (buf))) {
        fprintf (stderr, "%s: converter error %d: %s\n", 
                        cnv->filename, err, buf);
    } else {
        switch (err) {
        case -1:
            fprintf (stderr, "%s: error: converter could not open '%s'\n",
                progname, options.input_file);
            break;
        case -2:
            fprintf (stderr, "%s: error: read error\n", progname);
            break;
        case -4:
            fprintf (stderr, "%s: error: write error\n", progname);
            break;
        case -5:
            fprintf (stderr, "%s: error: %s: invalid input file\n", progname,
                options.input_file);
            break;
        case -8:
            fprintf (stderr, "%s: error: converter ran out of memory\n", 
                progname);
            break;
        case -13:
            fprintf (stderr, "%s: error: converter cannot create '%s'\n",
                progname, options.output_file);
            break;
        case -14:
            fprintf (stderr, 
                "%s: error: converter did not recognise input file '%s'\n",
                progname, options.input_file);
            break;
        default:
            fprintf (stderr, "%s: error: converter returned error %x\n", 
                cnv->filename, err);
        }
    }
}

static int init_converter (struct converter *cnv)
{
    char    path[MAX_PATH];
    int     ret;

    sprintf (path, "%s/%s", cnv_dir, cnv->filename);

    cnv->handle = LoadLibrary (path);
    if (cnv->handle == (HINSTANCE)HINSTANCE_ERROR) {
        if (verbose > 1)
            fprintf (stderr, "%s: error: LoadLibrary failed\n", cnv->filename);
        return 1;
    }

    cnv->InitConverter = (InitConverter32*)
        GetProcAddress (cnv->handle, "InitConverter32");
    cnv->IsFormatCorrect = (IsFormatCorrect32*)
        GetProcAddress (cnv->handle, "IsFormatCorrect32");
    cnv->ForeignToRtf = (ForeignToRtf32*)
        GetProcAddress (cnv->handle, "ForeignToRtf32");
    cnv->RtfToForeign = (RtfToForeign32*)
        GetProcAddress (cnv->handle, "RtfToForeign32");
    cnv->FetchError = (CchFetchLpszError*)
        GetProcAddress (cnv->handle, "CchFetchLpszError");
    cnv->FreeConverter = (UninitConverter*)
        GetProcAddress (cnv->handle, "UninitConverter");
    cnv->GetRead = (GetReadNames*)
        GetProcAddress (cnv->handle, "GetReadNames");
    cnv->GetWrite = (GetWriteNames*)
        GetProcAddress (cnv->handle, "GetWriteNames");
    cnv->RegisterApp = (RegisterApp*) 
        GetProcAddress (cnv->handle, "RegisterApp");
    
    if (!cnv->InitConverter) {
        fprintf (stderr, 
            "%s: error: InitConverter32 could not be resolved\n", path);
        FreeLibrary (cnv->handle);
        return 1;
    }

    ret = cnv->InitConverter ((HANDLE)NULL, "WORDCNV");
    if (ret != 1) {
        print_cnv_error (ret, cnv);
        FreeLibrary (cnv->handle);
        return 1;
    }

    return 0;
}

static void free_converter (struct converter *cnv)
{
    if (cnv->FreeConverter) cnv->FreeConverter ();
    FreeLibrary (cnv->handle);
}

static int register_app (struct converter *cnv)
{
    HGLOBAL mem;
    BYTE *p, opcode, len;
    WORD minor, major;
    int size;

    if (verbose) {
        fprintf (stderr, "%s: info: calling RegisterApp\n", cnv->filename);
    }

    mem = cnv->RegisterApp (fRegAppSupportNonOem, NULL);
    if (mem == (HGLOBAL)NULL) {
        if (verbose) {
            fprintf (stderr, "%s: info: converter has no preferences\n", 
                cnv->filename);
        }
        return 0;
    }

    if (!verbose) {
        GlobalFree (mem);
        return 0;
    }

    p = GlobalLock (mem);
    size = *((WORD*)p) - 2;
    p += 2;

    while (size >= 2) {
        len = *p++;
        opcode = *p++;
        size -= len;
        switch (opcode) {
        case RegAppOpcodeVer:
            major = *((WORD*)p)++;
            minor = *((WORD*)p)++;
            fprintf (stderr, 
                "%s: info: converter supports Word %d.%d RTF\n", 
                cnv->filename, (int)major, (int)minor);
            break;
        case RegAppOpcodeDocfile:
            major = *((WORD*)p)++;
            fprintf (stderr, 
                "%s: info: converter does%ssupport OLE structured storage\n",
                cnv->filename, major & 1 ? " " : " not ");
            fprintf (stderr, 
                "%s: info: converter does%ssupport non-OLE files\n",
                cnv->filename, major & 2 ? " " : " not ");
            break;
        case RegAppOpcodecharset:
            major = *p++;
            fprintf (stderr, 
                "%s: info: converter expects filenames in charset %d\n",
                cnv->filename, (int)major);
            break;
        case RegAppOpcodeReloadOnSave:
            fprintf (stderr, 
                "%s: info: converter expects caller to reload file\n",
                cnv->filename);
            break;
        case RegAppOpcodePicPlacehold:
            fprintf (stderr, 
                "%s: info: converter expects picture placeholders\n", 
                cnv->filename);
            break;
        case RegAppOpcodeFavourUnicode:
            fprintf (stderr, "%s: info: converter favours unicode\n",
                cnv->filename);
            break;
        case RegAppOpcodeNoClassifychars:
            fprintf (stderr, 
                "%s: info: converter expects no classify characters\n",
                cnv->filename);
            break;
        default:
            fprintf (stderr, 
                "%s: warning: converter passed unknown preference %02x\n",
                cnv->filename, (int)opcode);
            break;
        }
    }


    GlobalUnlock (mem);
    GlobalFree (mem);

    return 0;
}

static void print_spaces (int i) 
{
    while (i-- >= 0) putc (' ', stdout);
}

static void print_classes (void)
{
    int type, max;
    int filename_max, class_name_max, description_max, extensions_max;
    struct ms_converter *ms;
    
    filename_max = class_name_max = description_max = extensions_max = -1;
    for (ms=msconverters; ms; ms=ms->next) {
        max = strlen (ms->filename);
        if (max > filename_max) filename_max = max;

        max = strlen (ms->class_name);
        if (max > class_name_max) class_name_max = max;

        max = strlen (ms->description);
        if (max > description_max) description_max = max;

        max = strlen (ms->extensions);
        if (max > extensions_max) extensions_max = max;
    }

    filename_max++; class_name_max++; description_max++; extensions_max++;

    for (type=MS_OTHER_TO_RTF; type<MS_LAST; type++) {
        printf ("%s filters: \n\n", 
            type == MS_OTHER_TO_RTF ?  "Import" : "Export");
        printf ("Filename");
        print_spaces (filename_max - strlen ("Filename"));
        printf ("Class");
        print_spaces (class_name_max - strlen ("Class"));
        printf ("Description");
        print_spaces (description_max - strlen ("Description"));
        printf ("Extensions\n");
    
        for (ms=msconverters; ms; ms=ms->next) {
            if (ms->type == type) {
                printf (ms->filename);
                print_spaces (filename_max - strlen (ms->filename));

                printf (ms->class_name);
                print_spaces (class_name_max - strlen(ms->class_name));

                printf (ms->description);
                print_spaces (description_max - strlen(ms->description));

                printf ("%s\n", ms->extensions);
            }
        }
        printf ("\n\n");
    }
}

static int get_classes (struct converter *cnv, char *class_name, char *desc, 
          char *extension, int type)
{
    HGLOBAL hClass, hDesc, hExt;
    char *p;

    hClass = GlobalAlloc (GHND, 1024);
    hDesc = GlobalAlloc (GHND, 1024);
    hExt = GlobalAlloc (GHND, 1024);

    if (type == MS_OTHER_TO_RTF) {
        if (cnv->GetRead) {
            cnv->GetRead (hClass, hDesc, hExt);
        } else {
            return 1;
        }
    } else if (type == MS_RTF_TO_OTHER) {
        if (cnv->GetWrite) {
            cnv->GetWrite (hClass, hDesc, hExt);
        } else {
            return 1;
        }
    } else {
        fprintf (stderr, "%s: error: internal error\n", 
            progname);
        return 2;
    }
    p = (char *) GlobalLock (hClass);
    strcpy_fix_double_null (class_name, p);
    GlobalUnlock (hClass);
    p = (char *) GlobalLock (hDesc);
    strcpy_fix_double_null (desc, p);
    GlobalUnlock (hDesc);
    p = (char *) GlobalLock (hExt);
    strcpy_fix_double_null (extension, p);
    GlobalUnlock (hExt);
    
    GlobalFree (hClass);
    GlobalFree (hDesc);
    GlobalFree (hExt);

    return 0;
}

static int read_converter_dlls (void)
{
    char *cnv_tmp, *extension, *class_name, *desc;
    char extension_buf[MAX_PATH], class_name_buf[MAX_PATH];
    char desc_buf[MAX_PATH], file_name_buf[MAX_PATH];
    WIN32_FIND_DATA data;
    HANDLE hfind;
    struct converter cnv;
    struct ms_converter *ms_conv, *ms_conv_last;
    int type, msconverters_count;

    if (NULL != wordcnv_dir) 
        cnv_tmp = wordcnv_dir;
    else if (NULL == (cnv_tmp = getenv ("WORDCNV_DIR")))
        cnv_tmp = "C:\\Program Files\\Common Files\\Microsoft Shared\\Textconv";

    strcpy (cnv_dir, cnv_tmp);
    strcpy (file_name_buf, cnv_dir);
    strcat (file_name_buf, "\\*");

    if (verbose) {
        fprintf (stderr, "%s: info: searching '%s' for converter dlls...\n", 
                progname, cnv_dir);
    }

    hfind = FindFirstFile (file_name_buf, &data);
    if (hfind == INVALID_HANDLE_VALUE) {
        fprintf (stderr, "%s: error: FindFirstFile failed with error %x\n", 
                progname, (int)GetLastError ());
        return 1;
    }
                
    msconverters_count = 0;
    ms_conv_last = NULL;

    while (1) {
    /* we're only interested in .cnv files */
        if (strlen (data.cFileName) > 5) {
            extension = data.cFileName + strlen (data.cFileName) - 4;
        }
        else {
            extension = NULL;
        }

        if (extension && 
            (!stricmp (extension, ".cnv") || !stricmp (extension, ".wpc"))) {
        
            if (verbose) {
                printf ("%s: info: found: %s\n", progname, data.cFileName);
            }

            memset (&cnv, 0, sizeof (struct converter));
            strcpy (cnv.filename, data.cFileName);

            if (!init_converter (&cnv)) {

                for (type=MS_OTHER_TO_RTF;type!=MS_LAST;type++) {

                    if (get_classes (&cnv, class_name_buf, desc_buf, 
                        extension_buf, type)) {
                        continue;
                    }

                    class_name = class_name_buf;
                    desc = desc_buf;
                    extension = extension_buf;

                    while (*class_name && *desc && *extension) {
                        /* add them one by one */
                        ms_conv = (struct ms_converter*)
                            LocalAlloc (LPTR, sizeof (struct ms_converter));
                        if (!ms_conv) {
                            fprintf (stderr, "%s: error: out of memory\n", 
                                        progname);
                            return 1;
                        }
                        strcpy (ms_conv->filename, data.cFileName);
                        strcpy (ms_conv->class_name, class_name);
                        class_name += strlen (class_name) + 1;
                        strcpy (ms_conv->description, desc);
                        desc += strlen (desc) + 1;
                        strcpy (ms_conv->extensions, extension);
                        extension += strlen (extension) + 1;
                        ms_conv->type = type;
                        ms_conv->next = NULL;
                        if (ms_conv_last == NULL) {
                            msconverters = ms_conv;
                            ms_conv_last = ms_conv;
                        }
                        else {
                            ms_conv_last->next = ms_conv;
                            ms_conv_last = ms_conv;
                        }
                   }
               }

               msconverters_count++;
               free_converter (&cnv);
            }
        }
        if (!FindNextFile (hfind, &data)) {
            int ret;

            ret = GetLastError ();
            if (ret != ERROR_NO_MORE_FILES) {
                fprintf (stderr, "%s: error: FindNextFile returned %x\n", 
                    progname, ret);
            }
            FindClose (hfind);

            break;
        }
    }

    if (verbose) {
        printf ("%s: info: %d converter dlls found\n", 
            progname, msconverters_count);
    }

    return 0;
}

static HGLOBAL StringToHGLOBAL (const char *string)
{
    char *p;
    HGLOBAL hMem = (HGLOBAL)NULL;

    if (string != NULL) {
        hMem = GlobalAlloc (GHND, strlen (string) + 1);
        p = GlobalLock (hMem);
        strcpy (p, string);
        GlobalUnlock (hMem);
    }
 
    return hMem;
}

static long pascal write_progress (int cch, int percent)
{
    static int progress = -1;
    DWORD size;

    if (percent != progress) {
        printf ("\x08\x08\x08\x08%03d%%", percent);
        progress = percent;
    }
    if (cch) {
        char *p = GlobalLock (hout);
        if (!WriteFile (fout, p, cch, &size, NULL)) {
            fprintf (stderr, "%s: error: cannot write: %x\n",
                options.output_file, (int)GetLastError ());
            GlobalUnlock (hout);
            return -1;
        }
        if (cch != (int)size) {
            fprintf (stderr, "%s: error: %d of %d bytes written\n",
                options.output_file, (int)size, cch);
            GlobalUnlock (hout);
            return -1;
        }
        GlobalUnlock (hout);
    }
    return 1;
}

static int convert_foreign_to_rtf (const char *filename, const char *class_name)
{
    int ret;
    HGLOBAL hFilename, hClass, hSubset;
    struct converter cnv;
    char buf[MAX_PATH];
    struct ms_converter *ms;

    for (ms=msconverters; ms; ms=ms->next) {
        if ((ms->type == MS_OTHER_TO_RTF) &&
            !stricmp (class_name, ms->class_name)) {
            break;
        }
    }
    if (!ms) {
        fprintf (stderr, "%s: error: Import class %s not found\n", 
                progname, class_name);
        return 1;
    }
    if (verbose) {
        fprintf (stderr, "%s: info: using %s\n", progname, ms->filename);
    }
    memset (&cnv, 0, sizeof (struct converter));
    strcpy (cnv.filename, ms->filename);

    if (init_converter (&cnv)) return 1; 

    register_app (&cnv);

    if (!cnv.ForeignToRtf) {
        fprintf (stderr, 
            "%s: error: ForeignToRtf could not be resolved\n", cnv.filename);
        return 1;
    }

    strcpy (buf, filename);
    hFilename = StringToHGLOBAL (buf);
    hClass = StringToHGLOBAL (class_name);
    hSubset = StringToHGLOBAL ("");
    hout = GlobalAlloc (GHND, 1024);

    printf ("Converting from %s (%s to RTF) 000%%", filename, class_name);
  
    ret = cnv.ForeignToRtf (hFilename, NULL, hout, hClass, 
                hSubset, write_progress);
    printf ("\n");

    GlobalFree (hFilename);
    GlobalFree (hSubset);
    GlobalFree (hout);
    GlobalFree (hClass);

    if (ret != 0) {
        print_cnv_error (ret, &cnv);
        return 1;
    }

    return 0;
}

static long pascal read_progress (int flags, int percent)
{
    DWORD size;
    char *p;

    p = GlobalLock (hin);
    if (!ReadFile (fin, p, 512, &size, NULL)) {
        fprintf (stderr, "%s: error: cannot read file: %x\n",
            options.input_file, (int)GetLastError ());
        return -1;
    }
    GlobalUnlock (hin);

    return size;
}

static int convert_rtf_to_foreign (const char *filename, const char *class_name)
{
    int ret;
    HGLOBAL hFilename, hClass;
    struct converter cnv;
    char buf[MAX_PATH];
    struct ms_converter *ms;

    for (ms=msconverters; ms; ms=ms->next) {
        if ((ms->type == MS_RTF_TO_OTHER) &&
            !stricmp (class_name, ms->class_name)) {
            break;
        }
    }
    if (!ms) {
        fprintf (stderr, "Export class %s not found\n", class_name);
        return 1;
    }
    if (verbose) {
        fprintf (stderr, "%s: info: using %s\n", progname, ms->filename);
    }
    memset (&cnv, 0, sizeof (struct converter));
    strcpy (cnv.filename, ms->filename);

    if (init_converter (&cnv)) return 1; 

    register_app (&cnv);

    if (!cnv.RtfToForeign) {
        fprintf (stderr, 
            "%s: RtfToForeign could not be resolved\n", cnv.filename);
        return 1;
    }

    strcpy (buf, filename);
    hFilename = StringToHGLOBAL (buf);
    hClass = StringToHGLOBAL (class_name);

    hin = GlobalAlloc (GHND, 1024);

    printf ("Converting to %s (RTF to %s)\n", filename, ms->class_name);
  
    ret = cnv.RtfToForeign (hFilename, NULL, hin, hClass, read_progress);

    GlobalFree (hFilename);
    GlobalFree (hout);
    GlobalFree (hClass);

    if (ret != 0) {
        print_cnv_error (ret, &cnv);
        return 1;
    }

    return 0;
}

static int identify_file (const char *filename, char *class_name)
{
    HGLOBAL hFile, hClass;
    char    *p;
    int     ret;
    struct converter    cnv;
    HANDLE  file;
    DWORD   len;
    char    buf[100];
    struct ms_converter *ms;
     

    file = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
    if (file == INVALID_HANDLE_VALUE) {
        fprintf (stderr, "%s: error: %x\n", filename, (int)GetLastError ());
        return 2;
    }
    if (!ReadFile (file, buf, 5, &len, NULL)) {
        fprintf (stderr, "%s: error: %x\n", filename, (int)GetLastError ());
        return 2;
    }
    CloseHandle (file);

    if (len == 5 && !strncmp ("{\\rtf", buf, 5)) {
        fprintf (stderr, "%s recognised as RTF\n", filename);
        if (class_name) strcpy (class_name, "RTF");
        return 0;
    }

    hFile = GlobalAlloc (GHND, strlen (filename) + 1);
    p = GlobalLock (hFile);
    strcpy (p, filename);
    GlobalUnlock (hFile);

    hClass = GlobalAlloc (GHND, 1024);

    memset (&cnv, 0, sizeof (struct converter));
    
    for (ms=msconverters; ms; ms=ms->next) {
        if (ms->type != MS_OTHER_TO_RTF) continue;

        if (stricmp (cnv.filename, ms->filename)) {
            strcpy (cnv.filename, ms->filename);

            if (init_converter (&cnv) ) continue;

            if (cnv.IsFormatCorrect) {
                ret = cnv.IsFormatCorrect (hFile, hClass);
                if (ret == 1) {
                    fprintf (stderr, "%s: info: recognised %s as class %s, %s\n",
                        ms->filename, filename, ms->class_name, ms->description);

                    if (class_name) strcpy (class_name, ms->class_name);
                    free_converter (&cnv); 
                    GlobalFree (hFile);
                    GlobalFree (hClass);
                    
                    return 0;
                } else if (!(ret == 0 || ret == -1)) {
                    print_cnv_error (ret, &cnv);
                }
            }
    
            free_converter (&cnv); 
        }
    }
    GlobalFree (hFile);
    GlobalFree (hClass);

	fprintf (stderr, "%s: error: none of the converters recognised your file\n",
			progname);

    return -1;
}

static void usage (void)
{
    printf (
"wordcnv 1.1, a non-interactive file converter\n"
"\n"
"Usage: %s [OPTION]... [FILE] [FILE]\n"
"\n"
"  -h        Print this help\n"
"  -l        List the available converters\n"
"  -f FILE   Use IsFormatCorrect32 to find the format\n"
"  -i CLASS  Read input as format CLASS\n"
"  -x CLASS  Write output in format CLASS\n", progname);
}

static void make_absolute (char *filename)
{
    char buf[MAX_PATH], cwd[MAX_PATH];


    if (filename[0] == '/' || filename[0] == '\\' || filename[1] == ':')
        return;

    if (GetCurrentDirectory (sizeof (cwd), cwd)) {
        sprintf (buf, "%s\\%s", cwd, filename);
        strcpy (filename, buf);
        if (verbose) {
            fprintf (stderr, "%s: info: output file %s\n", progname, buf);
        }
    }
}

int main (int argc, char **argv) 
{
    int ret, i, n, last;

    progname = argv[0];

    memset (&options, 0, sizeof (options));
    strcpy (options.export_class, "RTF");

    for (i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            last = 0;
            for (n=1; !last && argv[i][n]; n++) {
                switch (argv[i][n]) {
                default:
                    fprintf (stderr, "%s: error: unknown option '-%c'\n",
                            progname, argv[i][n]);
                    return 1;
                case 'h':
                    usage ();
                    return 0;
                case 'l':
                    options.list = 1;
                    break;
                case 'f':
                    if (argv[i][n+1] || (i + 1) >= argc) {
                        usage ();
                        return 1;
                    }
                    strcpy (options.input_file, argv[++i]);
                    options.identify = 1;
                    options.files++;
                    last = 1;
                    break;
                case 'i':
                    if (argv[i][n+1] || (i + 1) >= argc) {
                        usage ();
                        return 1;
                    }
                    strcpy (options.import_class, argv[++i]);
                    last = 1;
                    break;
                case 'x':
                    if (argv[i][n+1] || (i + 1) >= argc) {
                        usage ();
                        return 1;
                    }
                    strcpy (options.export_class, argv[++i]);
                    last = 1;
                    break;
                case 'v':
                    verbose++;
                    break;
                }
            }
        }
        else if (options.files == 0) {
            strcpy (options.input_file, argv[i]);
            options.files++;
        } 
        else if (options.files == 1) {
            strcpy (options.output_file, argv[i]);
            options.files++;
        }
        else {
            usage ();
            return 1;
        }
    }

    if (verbose) fprintf (stderr, "%s: info: wordcnv 1.1\n", progname);

    if (read_converter_dlls ()) {
        return 1;
    }

    if (options.list) {
        print_classes ();
    }
    else if (options.identify) {
        if (options.files != 1) {
            usage ();
            return 2;
        }
        identify_file (options.input_file, NULL);
    }
    else {
        if (options.files != 2) {
            usage ();
            return 2;
        }
        if (options.output_file[0]) make_absolute (options.output_file);

        if (!options.import_class[0]) {
            if (identify_file (options.input_file, options.import_class)) {
                return 1;
            }
        }
        if (!stricmp (options.import_class, options.export_class)) {
            fprintf (stderr, "%s: error: Nothing to do; "
                            "import and export class are the same\n",
                progname);
            return 1;
        }

        if (!stricmp (options.import_class, "RTF")) {
            fin = CreateFile (options.input_file, GENERIC_READ, 
                FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 
                (HANDLE)NULL);
            if (fin == INVALID_HANDLE_VALUE) {
                fprintf (stderr, "%s: error: cannot open file: %x\n", 
                        options.input_file, (int)GetLastError ());
                return 1;
            }
            convert_rtf_to_foreign (options.output_file, options.export_class);
            CloseHandle (fin);
        }
        else if (!stricmp (options.export_class, "RTF")) {
            fout = CreateFile (options.output_file, GENERIC_WRITE, 0, 
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
            if (fout == INVALID_HANDLE_VALUE) {
                fprintf (stderr, "%s: error: cannot open file: %x\n", 
                        options.input_file, (int)GetLastError ());
                return 1;
            }
            convert_foreign_to_rtf (options.input_file, options.import_class);
            CloseHandle (fout);
        }
        else {
            char    temp_path[MAX_PATH], temp_file[MAX_PATH];

            /* why are we using the truly useless Win32 API for this? */
            if (!GetTempPath (sizeof (temp_path), temp_path)) {
                 fprintf (stderr, "%s: error: GetTempPath returned %x\n", 
                     progname, (int)GetLastError ());
                 return 1;
            }
            if (!GetTempFileName (temp_path, "cnv", rand (), temp_file)) {
                 fprintf (stderr, "%s: error: GetTempFileName returned %x\n", 
                     progname, (int)GetLastError ());
                 return 1;
            }

            fout = CreateFile (temp_file, GENERIC_WRITE, 0, 
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
            if (fout == INVALID_HANDLE_VALUE) {
                fprintf (stderr, "%s: error: cannot open temporary file %x\n",
                    temp_file, (int)GetLastError ());

                return 1;
            }
            if (verbose) {
                fprintf (stderr, "%s: info: using '%s' as temporary file\n", 
                    progname, temp_file);
            }

            ret = convert_foreign_to_rtf (options.input_file, 
                            options.import_class);
            CloseHandle (fout);

            if (!ret) {
                fin = CreateFile (temp_file, GENERIC_READ, FILE_SHARE_READ, 
                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);

                if (fin == INVALID_HANDLE_VALUE) {
                    fprintf (stderr, 
                        "%s: error: cannot open temporary file %x\n",
                        temp_file, (int)GetLastError ());
                    DeleteFile (temp_file);
                    return 1;
                }
                convert_rtf_to_foreign (options.output_file, 
                                options.export_class);
                CloseHandle (fin);
            }
            DeleteFile (temp_file);
        }
    }
    
    return 0;
}

