1//
2// Copyright(C) 2005-2014 Simon Howard
3//
4// This program is free software; you can redistribute it and/or
5// modify it under the terms of the GNU General Public License
6// as published by the Free Software Foundation; either version 2
7// of the License, or (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <sys/wait.h>
22
23#include <AppKit/AppKit.h>
24
25#include "config.h"
26
27#define RESPONSE_FILE "/tmp/launcher.rsp"
28#define TEMP_SCRIPT "/tmp/tempscript.sh"
29#define WINDOW_TITLE PACKAGE_STRING " command prompt"
30
31static char *executable_path;
32
33// Called on startup to save the location of the launcher program
34// (within a package, other executables should be in the same directory)
35
36void SetProgramLocation(const char *path)
37{
38    char *p;
39
40    executable_path = strdup(path);
41
42    p = strrchr(executable_path, '/');
43    *p = '\0';
44}
45
46// Write out the response file containing command line arguments.
47
48static void WriteResponseFile(const char *iwad, const char *args)
49{
50    FILE *fstream;
51
52    fstream = fopen(RESPONSE_FILE, "w");
53
54    if (iwad != NULL)
55    {
56        fprintf(fstream, "-iwad \"%s\"", iwad);
57    }
58
59    if (args != NULL)
60    {
61        fprintf(fstream, "%s", args);
62    }
63
64    fclose(fstream);
65}
66
67static void DoExec(const char *executable, const char *iwad, const char *args)
68{
69    char *argv[3];
70
71    asprintf(&argv[0], "%s/%s", executable_path, executable);
72
73    if (iwad != NULL || args != NULL)
74    {
75        WriteResponseFile(iwad, args);
76
77        argv[1] = "@" RESPONSE_FILE;
78        argv[2] = NULL;
79    }
80    else
81    {
82        argv[1] = NULL;
83    }
84
85    execv(argv[0], argv);
86    exit(-1);
87}
88
89// Execute the specified executable contained in the same directory
90// as the launcher, with the specified arguments.
91
92void ExecuteProgram(const char *executable, const char *iwad, const char *args)
93{
94    pid_t childpid;
95    char *homedir;
96
97    childpid = fork();
98
99    if (childpid == 0)
100    {
101        signal(SIGCHLD, SIG_DFL);
102
103        // Change directory to home dir before launch, so that any demos
104        // are saved somewhere sensible.
105
106        homedir = getenv("HOME");
107
108        if (homedir != NULL)
109        {
110            chdir(homedir);
111        }
112
113        DoExec(executable, iwad, args);
114    }
115    else
116    {
117        signal(SIGCHLD, SIG_IGN);
118    }
119}
120
121// Write a sequence of commands that will display the specified message
122// via shell commands.
123
124static void WriteMessage(FILE *script, char *msg)
125{
126    char *p;
127
128    fprintf(script, "echo \"");
129
130    for (p=msg; *p != '\0'; ++p)
131    {
132        // Start new line?
133
134        if (*p == '\n')
135        {
136            fprintf(script, "\"\necho \"");
137            continue;
138        }
139
140        // Escaped character?
141
142        if (*p == '\\' || *p == '\"')
143        {
144            fprintf(script, "\\");
145        }
146
147        fprintf(script, "%c", *p);
148    }
149
150    fprintf(script, "\"\n");
151}
152
153// Open a terminal window with the PATH set appropriately, and DOOMWADPATH
154// set to the specified value.
155
156void OpenTerminalWindow(const char *doomwadpath)
157{
158    FILE *stream;
159
160    // Generate a shell script that sets the PATH to include the location
161    // where the Doom binaries are, and DOOMWADPATH to include the
162    // IWAD files that have been configured in the launcher interface.
163    // The script then deletes itself and starts a shell.
164
165    stream = fopen(TEMP_SCRIPT, "w");
166
167    fprintf(stream, "#!/bin/sh\n");
168    //fprintf(stream, "set -x\n");
169    fprintf(stream, "PATH=\"%s:$PATH\"\n", executable_path);
170
171    // MANPATH is set to point to the directory within the bundle that
172    // contains the Unix manpages.  However, the bundle name or path to
173    // it can contain a space, and OS X doesn't like this!  As a
174    // workaround, create a symlink in /tmp to point to the real directory,
175    // and put *this* in MANPATH.
176
177    fprintf(stream, "rm -f \"/tmp/%s.man\"\n", PACKAGE_TARNAME);
178    fprintf(stream, "ln -s \"%s/man\" \"/tmp/%s.man\"\n",
179                    executable_path, PACKAGE_TARNAME);
180    fprintf(stream, "MANPATH=\"/tmp/%s.man:$(manpath)\"\n", PACKAGE_TARNAME);
181    fprintf(stream, "export MANPATH\n");
182
183    fprintf(stream, "DOOMWADPATH=\"%s\"\n", doomwadpath);
184    fprintf(stream, "export DOOMWADPATH\n");
185    fprintf(stream, "rm -f \"%s\"\n", TEMP_SCRIPT);
186
187    // Window title to something more interesting than "tempscript":
188    fprintf(stream, "echo -en \"\\033]0;%s\\a\"\n", WINDOW_TITLE);
189
190    // Display a useful message:
191
192    fprintf(stream, "clear\n");
193    WriteMessage(stream,
194        "\n"
195        "This command line has the PATH variable configured so that you may\n"
196        "launch the game with whatever parameters you desire.\n"
197        "\n"
198        "For example:\n"
199        "\n"
200        "   " PACKAGE_TARNAME " -iwad doom2.wad -file sid.wad -warp 1\n"
201        "\n"
202        "Type 'exit' to exit.\n");
203
204    fprintf(stream, "exec $SHELL\n");
205    fprintf(stream, "\n");
206
207    fclose(stream);
208
209    chmod(TEMP_SCRIPT, 0755);
210
211    // Tell the terminal to open a window to run the script.
212
213    [[NSWorkspace sharedWorkspace] openFile: @TEMP_SCRIPT
214                                   withApplication: @"Terminal"];
215}
216
217void OpenDocumentation(const char *filename)
218{
219    NSString *path;
220
221    path = [NSString stringWithFormat: @"%s/Documentation/%s",
222                     executable_path, filename];
223
224    [[NSWorkspace sharedWorkspace] openFile: path];
225}
226
227