1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <process.h>
19#include <string.h>
20#include <stdlib.h>
21#include <sys/types.h>
22#include <dirent.h>
23
24typedef char bool;
25#define false 0
26#define true (!false)
27
28bool silent = false;
29bool shared = false;
30bool export_all = false;
31enum mode_t { mCompile, mLink, mInstall };
32enum output_type_t { otGeneral, otObject, otProgram, otStaticLibrary, otDynamicLibrary };
33
34#ifdef __EMX__
35#  define SHELL_CMD  "sh"
36#  define CC         "gcc"
37#  define GEN_EXPORTS "emxexp"
38#  define DEF2IMPLIB_CMD "emximp"
39#  define SHARE_SW   "-Zdll -Zmtd"
40#  define USE_OMF true
41#  define TRUNCATE_DLL_NAME
42#  define DYNAMIC_LIB_EXT "dll"
43#  define EXE_EXT ".exe"
44
45#  if USE_OMF
46     /* OMF is the native format under OS/2 */
47#    define STATIC_LIB_EXT "lib"
48#    define OBJECT_EXT     "obj"
49#    define LIBRARIAN      "emxomfar"
50#  else
51     /* but the alternative, a.out, can fork() which is sometimes necessary */
52#    define STATIC_LIB_EXT "a"
53#    define OBJECT_EXT     "o"
54#    define LIBRARIAN      "ar"
55#  endif
56#endif
57
58
59typedef struct {
60    char *arglist[1024];
61    int num_args;
62    enum mode_t mode;
63    enum output_type_t output_type;
64    char *output_name;
65    char *stub_name;
66    char *tmp_dirs[1024];
67    int num_tmp_dirs;
68    char *obj_files[1024];
69    int num_obj_files;
70} cmd_data_t;
71
72void parse_args(int argc, char *argv[], cmd_data_t *cmd_data);
73bool parse_long_opt(char *arg, cmd_data_t *cmd_data);
74int parse_short_opt(char *arg, cmd_data_t *cmd_data);
75bool parse_input_file_name(char *arg, cmd_data_t *cmd_data);
76bool parse_output_file_name(char *arg, cmd_data_t *cmd_data);
77void post_parse_fixup(cmd_data_t *cmd_data);
78bool explode_static_lib(char *lib, cmd_data_t *cmd_data);
79int execute_command(cmd_data_t *cmd_data);
80char *shell_esc(const char *str);
81void cleanup_tmp_dirs(cmd_data_t *cmd_data);
82void generate_def_file(cmd_data_t *cmd_data);
83char *nameof(char *fullpath);
84char *truncate_dll_name(char *path);
85
86
87int main(int argc, char *argv[])
88{
89    int rc;
90    cmd_data_t cmd_data;
91
92    memset(&cmd_data, 0, sizeof(cmd_data));
93    cmd_data.mode = mCompile;
94    cmd_data.output_type = otGeneral;
95
96    parse_args(argc, argv, &cmd_data);
97    rc = execute_command(&cmd_data);
98
99    if (rc == 0 && cmd_data.stub_name) {
100        fopen(cmd_data.stub_name, "w");
101    }
102
103    cleanup_tmp_dirs(&cmd_data);
104    return rc;
105}
106
107
108
109void parse_args(int argc, char *argv[], cmd_data_t *cmd_data)
110{
111    int a;
112    char *arg;
113    bool argused;
114
115    for (a=1; a < argc; a++) {
116        arg = argv[a];
117        argused = false;
118
119        if (arg[0] == '-') {
120            if (arg[1] == '-') {
121                argused = parse_long_opt(arg + 2, cmd_data);
122            } else if (arg[1] == 'o' && a+1 < argc) {
123                cmd_data->arglist[cmd_data->num_args++] = arg;
124                arg = argv[++a];
125                argused = parse_output_file_name(arg, cmd_data);
126            } else {
127                int num_used = parse_short_opt(arg + 1, cmd_data);
128                argused = num_used > 0;
129
130                if (num_used > 1) {
131                    a += num_used - 1;
132                }
133            }
134        } else {
135            argused = parse_input_file_name(arg, cmd_data);
136        }
137
138        if (!argused) {
139            cmd_data->arglist[cmd_data->num_args++] = arg;
140        }
141    }
142
143    post_parse_fixup(cmd_data);
144}
145
146
147
148bool parse_long_opt(char *arg, cmd_data_t *cmd_data)
149{
150    char *equal_pos = strchr(arg, '=');
151    char var[50];
152    char value[500];
153
154    if (equal_pos) {
155        strncpy(var, arg, equal_pos - arg);
156        var[equal_pos - arg] = 0;
157        strcpy(value, equal_pos + 1);
158    } else {
159        strcpy(var, arg);
160    }
161
162    if (strcmp(var, "silent") == 0) {
163        silent = true;
164    } else if (strcmp(var, "mode") == 0) {
165        if (strcmp(value, "compile") == 0) {
166            cmd_data->mode = mCompile;
167            cmd_data->output_type = otObject;
168        }
169
170        if (strcmp(value, "link") == 0) {
171            cmd_data->mode = mLink;
172        }
173
174        if (strcmp(value, "install") == 0) {
175            cmd_data->mode = mInstall;
176        }
177    } else if (strcmp(var, "shared") == 0) {
178        shared = true;
179    } else if (strcmp(var, "export-all") == 0) {
180        export_all = true;
181    } else {
182        return false;
183    }
184
185    return true;
186}
187
188
189
190int parse_short_opt(char *arg, cmd_data_t *cmd_data)
191{
192    if (strcmp(arg, "export-dynamic") == 0) {
193        return 1;
194    }
195
196    if (strcmp(arg, "module") == 0) {
197        return 1;
198    }
199
200    if (strcmp(arg, "Zexe") == 0) {
201        return 1;
202    }
203
204    if (strcmp(arg, "avoid-version") == 0) {
205        return 1;
206    }
207
208    if (strcmp(arg, "prefer-pic") == 0) {
209        return 1;
210    }
211
212    if (strcmp(arg, "prefer-non-pic") == 0) {
213        return 1;
214    }
215
216    if (strcmp(arg, "version-info") == 0 ) {
217        return 2;
218    }
219
220    if (strcmp(arg, "no-install") == 0) {
221        return 1;
222    }
223
224    return 0;
225}
226
227
228
229bool parse_input_file_name(char *arg, cmd_data_t *cmd_data)
230{
231    char *ext = strrchr(arg, '.');
232    char *name = strrchr(arg, '/');
233    int pathlen;
234    char *newarg;
235
236    if (!ext) {
237        return false;
238    }
239
240    ext++;
241
242    if (name == NULL) {
243        name = strrchr(arg, '\\');
244
245        if (name == NULL) {
246            name = arg;
247        } else {
248            name++;
249        }
250    } else {
251        name++;
252    }
253
254    pathlen = name - arg;
255
256    if (strcmp(ext, "lo") == 0) {
257        newarg = (char *)malloc(strlen(arg) + 10);
258        strcpy(newarg, arg);
259        strcpy(newarg + (ext - arg), OBJECT_EXT);
260        cmd_data->arglist[cmd_data->num_args++] = newarg;
261        cmd_data->obj_files[cmd_data->num_obj_files++] = newarg;
262        return true;
263    }
264
265    if (strcmp(ext, "la") == 0) {
266        newarg = (char *)malloc(strlen(arg) + 10);
267        strcpy(newarg, arg);
268        newarg[pathlen] = 0;
269        strcat(newarg, ".libs/");
270
271        if (strncmp(name, "lib", 3) == 0) {
272            name += 3;
273        }
274
275        strcat(newarg, name);
276        ext = strrchr(newarg, '.') + 1;
277
278        if (shared && cmd_data->mode == mInstall) {
279          strcpy(ext, DYNAMIC_LIB_EXT);
280          newarg = truncate_dll_name(newarg);
281        } else {
282          strcpy(ext, STATIC_LIB_EXT);
283        }
284
285        cmd_data->arglist[cmd_data->num_args++] = newarg;
286        return true;
287    }
288
289    if (strcmp(ext, "c") == 0) {
290        if (cmd_data->stub_name == NULL) {
291            cmd_data->stub_name = (char *)malloc(strlen(arg) + 4);
292            strcpy(cmd_data->stub_name, arg);
293            strcpy(strrchr(cmd_data->stub_name, '.') + 1, "lo");
294        }
295    }
296
297    if (strcmp(name, CC) == 0 || strcmp(name, CC EXE_EXT) == 0) {
298        if (cmd_data->output_type == otGeneral) {
299            cmd_data->output_type = otObject;
300        }
301    }
302
303    return false;
304}
305
306
307
308bool parse_output_file_name(char *arg, cmd_data_t *cmd_data)
309{
310    char *name = strrchr(arg, '/');
311    char *ext = strrchr(arg, '.');
312    char *newarg = NULL, *newext;
313    int pathlen;
314
315    if (name == NULL) {
316        name = strrchr(arg, '\\');
317
318        if (name == NULL) {
319            name = arg;
320        } else {
321            name++;
322        }
323    } else {
324        name++;
325    }
326
327    if (!ext) {
328        cmd_data->stub_name = arg;
329        cmd_data->output_type = otProgram;
330        newarg = (char *)malloc(strlen(arg) + 5);
331        strcpy(newarg, arg);
332        strcat(newarg, EXE_EXT);
333        cmd_data->arglist[cmd_data->num_args++] = newarg;
334        cmd_data->output_name = newarg;
335        return true;
336    }
337
338    ext++;
339    pathlen = name - arg;
340
341    if (strcmp(ext, "la") == 0) {
342        cmd_data->stub_name = arg;
343        cmd_data->output_type = shared ? otDynamicLibrary : otStaticLibrary;
344        newarg = (char *)malloc(strlen(arg) + 10);
345        mkdir(".libs", 0);
346        strcpy(newarg, ".libs/");
347
348        if (strncmp(arg, "lib", 3) == 0) {
349            arg += 3;
350        }
351
352        strcat(newarg, arg);
353        newext = strrchr(newarg, '.') + 1;
354        strcpy(newext, shared ? DYNAMIC_LIB_EXT : STATIC_LIB_EXT);
355
356#ifdef TRUNCATE_DLL_NAME
357        if (shared) {
358          newarg = truncate_dll_name(newarg);
359        }
360#endif
361
362        cmd_data->arglist[cmd_data->num_args++] = newarg;
363        cmd_data->output_name = newarg;
364        return true;
365    }
366
367    if (strcmp(ext, "lo") == 0) {
368        cmd_data->stub_name = arg;
369        cmd_data->output_type = otObject;
370        newarg = (char *)malloc(strlen(arg) + 2);
371        strcpy(newarg, arg);
372        ext = strrchr(newarg, '.') + 1;
373        strcpy(ext, OBJECT_EXT);
374        cmd_data->arglist[cmd_data->num_args++] = newarg;
375        cmd_data->output_name = newarg;
376        return true;
377    }
378
379    return false;
380}
381
382
383
384void post_parse_fixup(cmd_data_t *cmd_data)
385{
386    int a;
387    char *arg;
388    char *ext;
389
390    if (cmd_data->output_type == otStaticLibrary && cmd_data->mode == mLink) {
391        /* We do a real hatchet job on the args when making a static library
392         * removing all compiler switches & any other cruft that ar won't like
393         * We also need to explode any libraries listed
394         */
395
396        for (a=0; a < cmd_data->num_args; a++) {
397            arg = cmd_data->arglist[a];
398
399            if (arg) {
400                ext = strrchr(arg, '.');
401
402                if (ext) {
403                    ext++;
404                }
405
406                if (arg[0] == '-') {
407                    cmd_data->arglist[a] = NULL;
408
409                    if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) {
410                        cmd_data->arglist[a+1] = NULL;
411                    }
412
413                    if (strcmp(arg, "-R") == 0 && a+1 < cmd_data->num_args) {
414                        cmd_data->arglist[a+1] = NULL;
415                    }
416
417                    if (strcmp(arg, "-version-info") == 0 && a+1 < cmd_data->num_args) {
418                        cmd_data->arglist[a+1] = NULL;
419                    }
420
421                    if (strcmp(arg, "-Zstack") == 0 && a+1 < cmd_data->num_args) {
422                        cmd_data->arglist[a+1] = NULL;
423                    }
424
425                    if (strcmp(arg, "-o") == 0) {
426                        a++;
427                    }
428                }
429
430                if (strcmp(arg, CC) == 0 || strcmp(arg, CC EXE_EXT) == 0) {
431                    cmd_data->arglist[a] = LIBRARIAN " cr";
432                }
433
434                if (ext) {
435                    if (strcmp(ext, "h") == 0 || strcmp(ext, "c") == 0) {
436                        /* ignore source files, they don't belong in a library */
437                        cmd_data->arglist[a] = NULL;
438                    }
439
440                    if (strcmp(ext, STATIC_LIB_EXT) == 0) {
441                        cmd_data->arglist[a] = NULL;
442                        explode_static_lib(arg, cmd_data);
443                    }
444                }
445            }
446        }
447    }
448
449    if (cmd_data->output_type == otDynamicLibrary) {
450        for (a=0; a < cmd_data->num_args; a++) {
451            arg = cmd_data->arglist[a];
452
453            if (arg) {
454                if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) {
455                    cmd_data->arglist[a] = NULL;
456                    cmd_data->arglist[a+1] = NULL;
457                }
458            }
459        }
460
461        if (export_all) {
462            generate_def_file(cmd_data);
463        }
464    }
465
466#if USE_OMF
467    if (cmd_data->output_type == otObject ||
468        cmd_data->output_type == otProgram ||
469        cmd_data->output_type == otDynamicLibrary) {
470        cmd_data->arglist[cmd_data->num_args++] = "-Zomf";
471    }
472#endif
473
474    if (shared && (cmd_data->output_type == otObject || cmd_data->output_type == otDynamicLibrary)) {
475        cmd_data->arglist[cmd_data->num_args++] = SHARE_SW;
476    }
477}
478
479
480
481int execute_command(cmd_data_t *cmd_data)
482{
483    int target = 0;
484    char *command;
485    int a, total_len = 0;
486    char *args[4];
487
488    for (a=0; a < cmd_data->num_args; a++) {
489        if (cmd_data->arglist[a]) {
490            total_len += strlen(cmd_data->arglist[a]) + 1;
491        }
492    }
493
494    command = (char *)malloc( total_len );
495    command[0] = 0;
496
497    for (a=0; a < cmd_data->num_args; a++) {
498        if (cmd_data->arglist[a]) {
499            strcat(command, cmd_data->arglist[a]);
500            strcat(command, " ");
501        }
502    }
503
504    command[strlen(command)-1] = 0;
505
506    if (!silent) {
507        puts(command);
508    }
509
510    cmd_data->num_args = target;
511    cmd_data->arglist[cmd_data->num_args] = NULL;
512    command = shell_esc(command);
513
514    args[0] = SHELL_CMD;
515    args[1] = "-c";
516    args[2] = command;
517    args[3] = NULL;
518    return spawnvp(P_WAIT, args[0], args);
519}
520
521
522
523char *shell_esc(const char *str)
524{
525    char *cmd;
526    unsigned char *d;
527    const unsigned char *s;
528
529    cmd = (char *)malloc(2 * strlen(str) + 1);
530    d = (unsigned char *)cmd;
531    s = (const unsigned char *)str;
532
533    for (; *s; ++s) {
534        if (*s == '"' || *s == '\\') {
535            *d++ = '\\';
536        }
537        *d++ = *s;
538    }
539
540    *d = '\0';
541    return cmd;
542}
543
544
545
546bool explode_static_lib(char *lib, cmd_data_t *cmd_data)
547{
548    char tmpdir[1024];
549    char savewd[1024];
550    char cmd[1024];
551    char *name;
552    DIR *dir;
553    struct dirent *entry;
554
555    strcpy(tmpdir, lib);
556    strcat(tmpdir, ".exploded");
557
558    mkdir(tmpdir, 0);
559    cmd_data->tmp_dirs[cmd_data->num_tmp_dirs++] = strdup(tmpdir);
560    getcwd(savewd, sizeof(savewd));
561
562    if (chdir(tmpdir) != 0)
563        return false;
564
565    strcpy(cmd, LIBRARIAN " x ");
566    name = strrchr(lib, '/');
567
568    if (name) {
569        name++;
570    } else {
571        name = lib;
572    }
573
574    strcat(cmd, "../");
575    strcat(cmd, name);
576    system(cmd);
577    chdir(savewd);
578    dir = opendir(tmpdir);
579
580    while ((entry = readdir(dir)) != NULL) {
581        if (entry->d_name[0] != '.') {
582            strcpy(cmd, tmpdir);
583            strcat(cmd, "/");
584            strcat(cmd, entry->d_name);
585            cmd_data->arglist[cmd_data->num_args++] = strdup(cmd);
586        }
587    }
588
589    closedir(dir);
590    return true;
591}
592
593
594
595void cleanup_tmp_dir(char *dirname)
596{
597    DIR *dir;
598    struct dirent *entry;
599    char fullname[1024];
600
601    dir = opendir(dirname);
602
603    if (dir == NULL)
604        return;
605
606    while ((entry = readdir(dir)) != NULL) {
607        if (entry->d_name[0] != '.') {
608            strcpy(fullname, dirname);
609            strcat(fullname, "/");
610            strcat(fullname, entry->d_name);
611            remove(fullname);
612        }
613    }
614
615    rmdir(dirname);
616}
617
618
619
620void cleanup_tmp_dirs(cmd_data_t *cmd_data)
621{
622    int d;
623
624    for (d=0; d < cmd_data->num_tmp_dirs; d++) {
625        cleanup_tmp_dir(cmd_data->tmp_dirs[d]);
626    }
627}
628
629
630
631void generate_def_file(cmd_data_t *cmd_data)
632{
633    char def_file[1024];
634    char implib_file[1024];
635    char *ext;
636    FILE *hDef;
637    char *export_args[1024];
638    int num_export_args = 0;
639    char *cmd;
640    int cmd_size = 0;
641    int a;
642
643    if (cmd_data->output_name) {
644        strcpy(def_file, cmd_data->output_name);
645        strcat(def_file, ".def");
646        hDef = fopen(def_file, "w");
647
648        if (hDef != NULL) {
649            fprintf(hDef, "LIBRARY '%s' INITINSTANCE\n", nameof(cmd_data->output_name));
650            fprintf(hDef, "DATA NONSHARED\n");
651            fprintf(hDef, "EXPORTS\n");
652            fclose(hDef);
653
654            for (a=0; a < cmd_data->num_obj_files; a++) {
655                cmd_size += strlen(cmd_data->obj_files[a]) + 1;
656            }
657
658            cmd_size += strlen(GEN_EXPORTS) + strlen(def_file) + 3;
659            cmd = (char *)malloc(cmd_size);
660            strcpy(cmd, GEN_EXPORTS);
661
662            for (a=0; a < cmd_data->num_obj_files; a++) {
663                strcat(cmd, " ");
664                strcat(cmd, cmd_data->obj_files[a] );
665            }
666
667            strcat(cmd, ">>");
668            strcat(cmd, def_file);
669            puts(cmd);
670            export_args[num_export_args++] = SHELL_CMD;
671            export_args[num_export_args++] = "-c";
672            export_args[num_export_args++] = cmd;
673            export_args[num_export_args++] = NULL;
674            spawnvp(P_WAIT, export_args[0], export_args);
675            cmd_data->arglist[cmd_data->num_args++] = strdup(def_file);
676
677            /* Now make an import library for the dll */
678            num_export_args = 0;
679            export_args[num_export_args++] = DEF2IMPLIB_CMD;
680            export_args[num_export_args++] = "-o";
681
682            strcpy(implib_file, ".libs/");
683            strcat(implib_file, cmd_data->stub_name);
684            ext = strrchr(implib_file, '.');
685
686            if (ext)
687                *ext = 0;
688
689            strcat(implib_file, ".");
690            strcat(implib_file, STATIC_LIB_EXT);
691
692            export_args[num_export_args++] = implib_file;
693            export_args[num_export_args++] = def_file;
694            export_args[num_export_args++] = NULL;
695            spawnvp(P_WAIT, export_args[0], export_args);
696        }
697    }
698}
699
700
701
702/* returns just a file's name without path or extension */
703char *nameof(char *fullpath)
704{
705    char buffer[1024];
706    char *ext;
707    char *name = strrchr(fullpath, '/');
708
709    if (name == NULL) {
710        name = strrchr(fullpath, '\\');
711    }
712
713    if (name == NULL) {
714        name = fullpath;
715    } else {
716        name++;
717    }
718
719    strcpy(buffer, name);
720    ext = strrchr(buffer, '.');
721
722    if (ext) {
723        *ext = 0;
724        return strdup(buffer);
725    }
726
727    return name;
728}
729
730
731
732char *truncate_dll_name(char *path)
733{
734    /* Cut DLL name down to 8 characters after removing any mod_ prefix */
735    char *tmppath = strdup(path);
736    char *newname = strrchr(tmppath, '/') + 1;
737    char *ext = strrchr(tmppath, '.');
738    int len;
739
740    if (ext == NULL)
741        return tmppath;
742
743    len = ext - newname;
744
745    if (strncmp(newname, "mod_", 4) == 0) {
746        strcpy(newname, newname + 4);
747        len -= 4;
748    }
749
750    if (len > 8) {
751        strcpy(newname + 8, strchr(newname, '.'));
752    }
753
754    return tmppath;
755}
756