1 | // This file is part of SmallBASIC |
2 | // |
3 | // lowlevel device (OS) I/O |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/device.h" |
11 | |
12 | #include <stdio.h> |
13 | #include <time.h> |
14 | |
15 | #if defined(_UnixOS) |
16 | #include <sys/time.h> |
17 | #include <sys/types.h> |
18 | #include <sys/stat.h> |
19 | #include <unistd.h> |
20 | #elif defined(_Win32) |
21 | #include <windows.h> |
22 | #include <process.h> |
23 | #include <dir.h> |
24 | #endif |
25 | |
26 | extern char **environ; |
27 | |
28 | #define BUFSIZE 1024 |
29 | |
30 | #if defined(_Win32) |
31 | |
32 | /** |
33 | * w32 run process and capture stdout/stderr using pipes |
34 | * |
35 | * returns a newly allocated string with the result or NULL |
36 | * |
37 | * warning: if the cmd is a GUI process, the shell will hang |
38 | */ |
39 | int shell(const char *cmd, var_t *r) { |
40 | HANDLE hPipeRead, hPipeWrite; |
41 | |
42 | SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) }; |
43 | // Pipe handles are inherited by child process. |
44 | saAttr.bInheritHandle = TRUE; |
45 | saAttr.lpSecurityDescriptor = NULL; |
46 | |
47 | // Create a pipe to get results from child's stdout. |
48 | if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0)) { |
49 | return 0; |
50 | } |
51 | |
52 | STARTUPINFO si = { sizeof(STARTUPINFO) }; |
53 | si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; |
54 | si.hStdOutput = hPipeWrite; |
55 | si.hStdError = hPipeWrite; |
56 | // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags. |
57 | si.wShowWindow = SW_HIDE; |
58 | |
59 | PROCESS_INFORMATION pi = { 0 }; |
60 | if (!CreateProcess(NULL, (LPSTR)cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { |
61 | CloseHandle(hPipeWrite); |
62 | CloseHandle(hPipeRead); |
63 | return 0; |
64 | } |
65 | |
66 | int processEnded = 0; |
67 | while (!processEnded) { |
68 | // Give some timeslice (50ms), so we won't waste 100% cpu. |
69 | processEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0; |
70 | |
71 | // Even if process exited - we continue reading, if there is some data available over pipe. |
72 | while (1) { |
73 | char buf[BUFSIZE]; |
74 | DWORD numRead = 0; |
75 | DWORD numAvail = 0; |
76 | |
77 | if (!PeekNamedPipe(hPipeRead, NULL, 0, NULL, &numAvail, NULL)) { |
78 | break; |
79 | } |
80 | |
81 | if (!numAvail) { |
82 | // no data available |
83 | break; |
84 | } |
85 | |
86 | if (!ReadFile(hPipeRead, buf, min(sizeof(buf) - 1, numAvail), &numRead, NULL) || |
87 | !numRead) { |
88 | // child process may have ended |
89 | break; |
90 | } |
91 | buf[numRead] = 0; |
92 | v_strcat(r, buf); |
93 | } |
94 | } |
95 | CloseHandle(hPipeWrite); |
96 | CloseHandle(hPipeRead); |
97 | CloseHandle(pi.hProcess); |
98 | CloseHandle(pi.hThread); |
99 | return 1; |
100 | } |
101 | |
102 | int dev_run(const char *cmd, var_t *r, int wait) { |
103 | int result = 1; |
104 | if (r != NULL) { |
105 | v_zerostr(r); |
106 | result = shell(cmd, r); |
107 | } else if (wait) { |
108 | STARTUPINFO si; |
109 | PROCESS_INFORMATION pi; |
110 | memset(&si, 0, sizeof(STARTUPINFO)); |
111 | si.cb = sizeof(STARTUPINFO); |
112 | si.dwFlags = STARTF_USESHOWWINDOW; |
113 | si.wShowWindow = SW_SHOWNORMAL; |
114 | if (CreateProcess(NULL, (LPSTR)cmd, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { |
115 | WaitForSingleObject(pi.hProcess, INFINITE); |
116 | CloseHandle(pi.hProcess); |
117 | CloseHandle(pi.hThread); |
118 | } else { |
119 | result = 0; |
120 | } |
121 | } else { |
122 | HWND hwnd = GetActiveWindow(); |
123 | ShellExecute(hwnd, "open" , cmd, 0, 0, SW_SHOWNORMAL); |
124 | } |
125 | return result; |
126 | } |
127 | |
128 | #else |
129 | int dev_run(const char *cmd, var_t *r, int wait) { |
130 | int result = 1; |
131 | if (r != NULL) { |
132 | v_zerostr(r); |
133 | FILE *fin = popen(cmd, "r" ); |
134 | if (fin) { |
135 | while (!feof(fin)) { |
136 | char buf[BUFSIZE + 1]; |
137 | int bytes = fread(buf, 1, BUFSIZE, fin); |
138 | buf[bytes] = '\0'; |
139 | v_strcat(r, buf); |
140 | } |
141 | pclose(fin); |
142 | } else { |
143 | result = 0; |
144 | } |
145 | } else if (wait) { |
146 | result = (system(cmd) != -1); |
147 | } |
148 | else if (fork() == 0) { |
149 | // exec separate process |
150 | int size = strlen(cmd) + 3; |
151 | char *src1 = malloc(size); |
152 | if (src1 != NULL) { |
153 | memset(src1, '\0', size); |
154 | // double quote the command |
155 | *src1 = '"'; |
156 | strcat(src1, cmd); |
157 | *(src1 + strlen(cmd) + 1) = '"'; |
158 | // -c means the next argument is the command string to execute |
159 | // this allow us to execute shell script |
160 | execlp("sh" , "sh" , "-c" , src1, NULL); |
161 | } |
162 | exit(-1); |
163 | } |
164 | return result; |
165 | } |
166 | #endif |
167 | |
168 | #if !defined(IMPL_DEV_ENV) |
169 | |
170 | int dev_setenv(const char *key, const char *value) { |
171 | #ifdef __MINGW32__ |
172 | // use leaky putenv |
173 | unsigned size = snprintf(NULL, 0, "%s=%s" , key, value) + 1; |
174 | char *buf = malloc(size); |
175 | buf[0] = '\0'; |
176 | snprintf(buf, size, "%s=%s" , key, value); |
177 | return putenv(buf); |
178 | #else |
179 | return setenv(key, value, 1); |
180 | #endif |
181 | } |
182 | |
183 | /** |
184 | * The getenv() function searches the environment list for a string that matches the string pointed |
185 | * to by name. The strings are of the form name = value. |
186 | * |
187 | * The getenv() function returns a pointer to the value in the environment, or NULL if there is no match. |
188 | */ |
189 | const char *dev_getenv(const char *str) { |
190 | return getenv(str); |
191 | } |
192 | |
193 | /** |
194 | * returns the number of environment variables |
195 | */ |
196 | int dev_env_count() { |
197 | int count = 0; |
198 | while (environ[count]) { |
199 | count++; |
200 | } |
201 | return count; |
202 | } |
203 | |
204 | /** |
205 | * returns the value of the n-th system's environment variable |
206 | */ |
207 | const char *dev_getenv_n(int n) { |
208 | int count = 0; |
209 | while (environ[count]) { |
210 | if (n == count) { |
211 | return environ[count]; |
212 | } |
213 | count++; |
214 | } |
215 | return NULL; |
216 | } |
217 | #endif |
218 | |
219 | uint32_t dev_get_millisecond_count(void) { |
220 | #if defined(__MACH__) |
221 | struct timeval t; |
222 | gettimeofday(&t, NULL); |
223 | return (uint32_t) (1000L * t.tv_sec + (t.tv_usec / 1000.0)); |
224 | #elif defined(_Win32) |
225 | return GetTickCount(); |
226 | #else |
227 | struct timespec t; |
228 | t.tv_sec = t.tv_nsec = 0; |
229 | if (0 == clock_gettime(CLOCK_MONOTONIC, &t)) { |
230 | return (uint32_t) (1000L * t.tv_sec + (t.tv_nsec / 1e6)); |
231 | } else { |
232 | struct timeval now; |
233 | gettimeofday(&now, NULL); |
234 | return (uint32_t) (1000L * now.tv_sec + (now.tv_usec / 1000.0)); |
235 | } |
236 | #endif |
237 | } |
238 | |
239 | |
240 | |