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    char *newarg;
234
235    if (!ext) {
236        return false;
237    }
238
239    ext++;
240
241    if (name == NULL) {
242        name = strrchr(arg, '\\');
243
244        if (name == NULL) {
245            name = arg;
246        } else {
247            name++;
248        }
249    } else {
250        name++;
251    }
252
253    if (strcmp(ext, "lo") == 0) {
254        newarg = (char *)malloc(strlen(arg) + 10);
255        strcpy(newarg, arg);
256        strcpy(newarg + (ext - arg), OBJECT_EXT);
257        cmd_data->arglist[cmd_data->num_args++] = newarg;
258        cmd_data->obj_files[cmd_data->num_obj_files++] = newarg;
259        return true;
260    }
261
262    if (strcmp(ext, "la") == 0) {
263        newarg = (char *)malloc(strlen(arg) + 10);
264        strcpy(newarg, arg);
265        newarg[pathlen] = 0;
266        strcat(newarg, ".libs/");
267
268        if (strncmp(name, "lib", 3) == 0) {
269            name += 3;
270        }
271
272        strcat(newarg, name);
273        ext = strrchr(newarg, '.') + 1;
274
275        if (shared && cmd_data->mode == mInstall) {
276          strcpy(ext, DYNAMIC_LIB_EXT);
277          newarg = truncate_dll_name(newarg);
278        } else {
279          strcpy(ext, STATIC_LIB_EXT);
280        }
281
282        cmd_data->arglist[cmd_data->num_args++] = newarg;
283        return true;
284    }
285
286    if (strcmp(ext, "c") == 0) {
287        if (cmd_data->stub_name == NULL) {
288            cmd_data->stub_name = (char *)malloc(strlen(arg) + 4);
289            strcpy(cmd_data->stub_name, arg);
290            strcpy(strrchr(cmd_data->stub_name, '.') + 1, "lo");
291        }
292    }
293
294    if (strcmp(name, CC) == 0 || strcmp(name, CC EXE_EXT) == 0) {
295        if (cmd_data->output_type == otGeneral) {
296            cmd_data->output_type = otObject;
297        }
298    }
299
300    return false;
301}
302
303
304
305bool parse_output_file_name(char *arg, cmd_data_t *cmd_data)
306{
307    char *name = strrchr(arg, '/');
308    char *ext = strrchr(arg, '.');
309    char *newarg = NULL, *newext;
310
311    if (name == NULL) {
312        name = strrchr(arg, '\\');
313
314        if (name == NULL) {
315            name = arg;
316        } else {
317            name++;
318        }
319    } else {
320        name++;
321    }
322
323    if (!ext) {
324        cmd_data->stub_name = arg;
325        cmd_data->output_type = otProgram;
326        newarg = (char *)malloc(strlen(arg) + 5);
327        strcpy(newarg, arg);
328        strcat(newarg, EXE_EXT);
329        cmd_data->arglist[cmd_data->num_args++] = newarg;
330        cmd_data->output_name = newarg;
331        return true;
332    }
333
334    ext++;
335
336    if (strcmp(ext, "la") == 0) {
337        cmd_data->stub_name = arg;
338        cmd_data->output_type = shared ? otDynamicLibrary : otStaticLibrary;
339        newarg = (char *)malloc(strlen(arg) + 10);
340        mkdir(".libs", 0);
341        strcpy(newarg, ".libs/");
342
343        if (strncmp(arg, "lib", 3) == 0) {
344            arg += 3;
345        }
346
347        strcat(newarg, arg);
348        newext = strrchr(newarg, '.') + 1;
349        strcpy(newext, shared ? DYNAMIC_LIB_EXT : STATIC_LIB_EXT);
350
351#ifdef TRUNCATE_DLL_NAME
352        if (shared) {
353          newarg = truncate_dll_name(newarg);
354        }
355#endif
356
357        cmd_data->arglist[cmd_data->num_args++] = newarg;
358        cmd_data->output_name = newarg;
359        return true;
360    }
361
362    if (strcmp(ext, "lo") == 0) {
363        cmd_data->stub_name = arg;
364        cmd_data->output_type = otObject;
365        newarg = (char *)malloc(strlen(arg) + 2);
366        strcpy(newarg, arg);
367        ext = strrchr(newarg, '.') + 1;
368        strcpy(ext, OBJECT_EXT);
369        cmd_data->arglist[cmd_data->num_args++] = newarg;
370        cmd_data->output_name = newarg;
371        return true;
372    }
373
374    return false;
375}
376
377
378
379void post_parse_fixup(cmd_data_t *cmd_data)
380{
381    int a;
382    char *arg;
383    char *ext;
384
385    if (cmd_data->output_type == otStaticLibrary && cmd_data->mode == mLink) {
386        /* We do a real hatchet job on the args when making a static library
387         * removing all compiler switches & any other cruft that ar won't like
388         * We also need to explode any libraries listed
389         */
390
391        for (a=0; a < cmd_data->num_args; a++) {
392            arg = cmd_data->arglist[a];
393
394            if (arg) {
395                ext = strrchr(arg, '.');
396
397                if (ext) {
398                    ext++;
399                }
400
401                if (arg[0] == '-') {
402                    cmd_data->arglist[a] = NULL;
403
404                    if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) {
405                        cmd_data->arglist[a+1] = NULL;
406                    }
407
408                    if (strcmp(arg, "-R") == 0 && a+1 < cmd_data->num_args) {
409                        cmd_data->arglist[a+1] = NULL;
410                    }
411
412                    if (strcmp(arg, "-version-info") == 0 && a+1 < cmd_data->num_args) {
413                        cmd_data->arglist[a+1] = NULL;
414                    }
415
416                    if (strcmp(arg, "-Zstack") == 0 && a+1 < cmd_data->num_args) {
417                        cmd_data->arglist[a+1] = NULL;
418                    }
419
420                    if (strcmp(arg, "-o") == 0) {
421                        a++;
422                    }
423                }
424
425                if (strcmp(arg, CC) == 0 || strcmp(arg, CC EXE_EXT) == 0) {
426                    cmd_data->arglist[a] = LIBRARIAN " cr";
427                }
428
429                if (ext) {
430                    if (strcmp(ext, "h") == 0 || strcmp(ext, "c") == 0) {
431                        /* ignore source files, they don't belong in a library */
432                        cmd_data->arglist[a] = NULL;
433                    }
434
435                    if (strcmp(ext, STATIC_LIB_EXT) == 0) {
436                        cmd_data->arglist[a] = NULL;
437                        explode_static_lib(arg, cmd_data);
438                    }
439                }
440            }
441        }
442    }
443
444    if (cmd_data->output_type == otDynamicLibrary) {
445        for (a=0; a < cmd_data->num_args; a++) {
446            arg = cmd_data->arglist[a];
447
448            if (arg) {
449                if (strcmp(arg, "-rpath") == 0 && a+1 < cmd_data->num_args) {
450                    cmd_data->arglist[a] = NULL;
451                    cmd_data->arglist[a+1] = NULL;
452                }
453            }
454        }
455
456        if (export_all) {
457            generate_def_file(cmd_data);
458        }
459    }
460
461#if USE_OMF
462    if (cmd_data->output_type == otObject ||
463        cmd_data->output_type == otProgram ||
464        cmd_data->output_type == otDynamicLibrary) {
465        cmd_data->arglist[cmd_data->num_args++] = "-Zomf";
466    }
467#endif
468
469    if (shared && (cmd_data->output_type == otObject || cmd_data->output_type == otDynamicLibrary)) {
470        cmd_data->arglist[cmd_data->num_args++] = SHARE_SW;
471    }
472}
473
474
475
476int execute_command(cmd_data_t *cmd_data)
477{
478    int target = 0;
479    char *command;
480    int a, total_len = 0;
481    char *args[4];
482
483    for (a=0; a < cmd_data->num_args; a++) {
484        if (cmd_data->arglist[a]) {
485            total_len += strlen(cmd_data->arglist[a]) + 1;
486        }
487    }
488
489    command = (char *)malloc( total_len );
490    command[0] = 0;
491
492    for (a=0; a < cmd_data->num_args; a++) {
493        if (cmd_data->arglist[a]) {
494            strcat(command, cmd_data->arglist[a]);
495            strcat(command, " ");
496        }
497    }
498
499    command[strlen(command)-1] = 0;
500
501    if (!silent) {
502        puts(command);
503    }
504
505    cmd_data->num_args = target;
506    cmd_data->arglist[cmd_data->num_args] = NULL;
507    command = shell_esc(command);
508
509    args[0] = SHELL_CMD;
510    args[1] = "-c";
511    args[2] = command;
512    args[3] = NULL;
513    return spawnvp(P_WAIT, args[0], args);
514}
515
516
517
518char *shell_esc(const char *str)
519{
520    char *cmd;
521    unsigned char *d;
522    const unsigned char *s;
523
524    cmd = (char *)malloc(2 * strlen(str) + 1);
525    d = (unsigned char *)cmd;
526    s = (const unsigned char *)str;
527
528    for (; *s; ++s) {
529        if (*s == '"' || *s == '\\') {
530            *d++ = '\\';
531        }
532        *d++ = *s;
533    }
534
535    *d = '\0';
536    return cmd;
537}
538
539
540
541bool explode_static_lib(char *lib, cmd_data_t *cmd_data)
542{
543    char tmpdir[1024];
544    char savewd[1024];
545    char cmd[1024];
546    char *name;
547    DIR *dir;
548    struct dirent *entry;
549
550    strcpy(tmpdir, lib);
551    strcat(tmpdir, ".exploded");
552
553    mkdir(tmpdir, 0);
554    cmd_data->tmp_dirs[cmd_data->num_tmp_dirs++] = strdup(tmpdir);
555    getcwd(savewd, sizeof(savewd));
556
557    if (chdir(tmpdir) != 0)
558        return false;
559
560    strcpy(cmd, LIBRARIAN " x ");
561    name = strrchr(lib, '/');
562
563    if (name) {
564        name++;
565    } else {
566        name = lib;
567    }
568
569    strcat(cmd, "../");
570    strcat(cmd, name);
571    system(cmd);
572    chdir(savewd);
573    dir = opendir(tmpdir);
574
575    while ((entry = readdir(dir)) != NULL) {
576        if (entry->d_name[0] != '.') {
577            strcpy(cmd, tmpdir);
578            strcat(cmd, "/");
579            strcat(cmd, entry->d_name);
580            cmd_data->arglist[cmd_data->num_args++] = strdup(cmd);
581        }
582    }
583
584    closedir(dir);
585    return true;
586}
587
588
589
590void cleanup_tmp_dir(char *dirname)
591{
592    DIR *dir;
593    struct dirent *entry;
594    char fullname[1024];
595
596    dir = opendir(dirname);
597
598    if (dir == NULL)
599        return;
600
601    while ((entry = readdir(dir)) != NULL) {
602        if (entry->d_name[0] != '.') {
603            strcpy(fullname, dirname);
604            strcat(fullname, "/");
605            strcat(fullname, entry->d_name);
606            remove(fullname);
607        }
608    }
609
610    rmdir(dirname);
611}
612
613
614
615void cleanup_tmp_dirs(cmd_data_t *cmd_data)
616{
617    int d;
618
619    for (d=0; d < cmd_data->num_tmp_dirs; d++) {
620        cleanup_tmp_dir(cmd_data->tmp_dirs[d]);
621    }
622}
623
624
625
626void generate_def_file(cmd_data_t *cmd_data)
627{
628    char def_file[1024];
629    char implib_file[1024];
630    char *ext;
631    FILE *hDef;
632    char *export_args[1024];
633    int num_export_args = 0;
634    char *cmd;
635    int cmd_size = 0;
636    int a;
637
638    if (cmd_data->output_name) {
639        strcpy(def_file, cmd_data->output_name);
640        strcat(def_file, ".def");
641        hDef = fopen(def_file, "w");
642
643        if (hDef != NULL) {
644            fprintf(hDef, "LIBRARY '%s' INITINSTANCE\n", nameof(cmd_data->output_name));
645            fprintf(hDef, "DATA NONSHARED\n");
646            fprintf(hDef, "EXPORTS\n");
647            fclose(hDef);
648
649            for (a=0; a < cmd_data->num_obj_files; a++) {
650                cmd_size += strlen(cmd_data->obj_files[a]) + 1;
651            }
652
653            cmd_size += strlen(GEN_EXPORTS) + strlen(def_file) + 3;
654            cmd = (char *)malloc(cmd_size);
655            strcpy(cmd, GEN_EXPORTS);
656
657            for (a=0; a < cmd_data->num_obj_files; a++) {
658                strcat(cmd, " ");
659                strcat(cmd, cmd_data->obj_files[a] );
660            }
661
662            strcat(cmd, ">>");
663            strcat(cmd, def_file);
664            puts(cmd);
665            export_args[num_export_args++] = SHELL_CMD;
666            export_args[num_export_args++] = "-c";
667            export_args[num_export_args++] = cmd;
668            export_args[num_export_args++] = NULL;
669            spawnvp(P_WAIT, export_args[0], export_args);
670            cmd_data->arglist[cmd_data->num_args++] = strdup(def_file);
671
672            /* Now make an import library for the dll */
673            num_export_args = 0;
674            export_args[num_export_args++] = DEF2IMPLIB_CMD;
675            export_args[num_export_args++] = "-o";
676
677            strcpy(implib_file, ".libs/");
678            strcat(implib_file, cmd_data->stub_name);
679            ext = strrchr(implib_file, '.');
680
681            if (ext)
682                *ext = 0;
683
684            strcat(implib_file, ".");
685            strcat(implib_file, STATIC_LIB_EXT);
686
687            export_args[num_export_args++] = implib_file;
688            export_args[num_export_args++] = def_file;
689            export_args[num_export_args++] = NULL;
690            spawnvp(P_WAIT, export_args[0], export_args);
691        }
692    }
693}
694
695
696
697/* returns just a file's name without path or extension */
698char *nameof(char *fullpath)
699{
700    char buffer[1024];
701    char *ext;
702    char *name = strrchr(fullpath, '/');
703
704    if (name == NULL) {
705        name = strrchr(fullpath, '\\');
706    }
707
708    if (name == NULL) {
709        name = fullpath;
710    } else {
711        name++;
712    }
713
714    strcpy(buffer, name);
715    ext = strrchr(buffer, '.');
716
717    if (ext) {
718        *ext = 0;
719        return strdup(buffer);
720    }
721
722    return name;
723}
724
725
726
727char *truncate_dll_name(char *path)
728{
729    /* Cut DLL name down to 8 characters after removing any mod_ prefix */
730    char *tmppath = strdup(path);
731    char *newname = strrchr(tmppath, '/') + 1;
732    char *ext = strrchr(tmppath, '.');
733    int len;
734
735    if (ext == NULL)
736        return tmppath;
737
738    len = ext - newname;
739
740    if (strncmp(newname, "mod_", 4) == 0) {
741        strcpy(newname, newname + 4);
742        len -= 4;
743    }
744
745    if (len > 8) {
746        strcpy(newname + 8, strchr(newname, '.'));
747    }
748
749    return tmppath;
750}
751