1 /*
2 * Copyright (c) 2006-2009, 2013-2015, 2019-2020 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Paul Mattes nor his contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * relink.c
32 * A Windows console-based 3270 Terminal Emulator
33 * Utility functions to read a session file and create a
34 * compatible shortcut.
35 */
36
37 #include "globals.h"
38
39 #include <signal.h>
40 #include "appres.h"
41 #include "3270ds.h"
42 #include "resources.h"
43 #include "ctlr.h"
44
45 #include "ctlrc.h"
46 #include "host.h"
47 #include "screen.h"
48 #include "task.h"
49 #include "trace.h"
50 #include "utils.h"
51
52 #include <wincon.h>
53
54 #include "winvers.h"
55 #include "shortcutc.h"
56 #include "windirs.h"
57
58 #include "relinkc.h"
59
60 codepages_t codepages[] = {
61 { "belgian", "500", 0, L"1252" },
62 { "belgian-euro", "1148", 0, L"1252" },
63 { "bracket", "37*", 0, L"1252" },
64 { "brazilian", "275", 0, L"1252" },
65 { "cp1047", "1047", 0, L"1252" },
66 { "cp870", "870", 0, L"1250" },
67 { "chinese-gb18030","1388", 1, L"936" },
68 { "finnish", "278", 0, L"1252" },
69 { "finnish-euro", "1143", 0, L"1252" },
70 { "french", "297", 0, L"1252" },
71 { "french-euro", "1147", 0, L"1252" },
72 { "german", "273", 0, L"1252" },
73 { "german-euro", "1141", 0, L"1252" },
74 { "greek", "875", 0, L"1253" },
75 { "hebrew", "424", 0, L"1255" },
76 { "icelandic", "871", 0, L"1252" },
77 { "icelandic-euro", "1149", 0, L"1252" },
78 { "italian", "280", 0, L"1252" },
79 { "italian-euro", "1144", 0, L"1252" },
80 { "japanese-kana", "930", 1, L"932" },
81 { "japanese-latin", "939", 1, L"932" },
82 { "norwegian", "277", 0, L"1252" },
83 { "norwegian-euro", "1142", 0, L"1252" },
84 { "russian", "880", 0, L"1251" },
85 { "simplified-chinese","935",1,L"936" },
86 { "spanish", "284", 0, L"1252" },
87 { "spanish-euro", "1145", 0, L"1252" },
88 { "thai", "1160", 0, L"874" },
89 { "traditional-chinese","937",1,L"950" },
90 { "turkish", "1026", 0, L"1254" },
91 { "uk", "285", 0, L"1252" },
92 { "uk-euro", "1146", 0, L"1252" },
93 { "us-euro", "1140", 0, L"1252" },
94 { "us-intl", "037", 0, L"1252" },
95 { NULL, NULL, 0, NULL }
96 };
97
98 size_t num_codepages = (sizeof(codepages) / sizeof(codepages[0])) - 1;
99
100 /* 2 3 4 5 */
101 int wrows[6] = { 0, 0,
102 MODEL_2_ROWS, MODEL_3_ROWS, MODEL_4_ROWS, MODEL_5_ROWS };
103 int wcols[6] = { 0, 0,
104 MODEL_2_COLS, MODEL_3_COLS, MODEL_4_COLS, MODEL_5_COLS };
105
106 static wchar_t *
reg_font_from_cset(char * cset,int * codepage)107 reg_font_from_cset(char *cset, int *codepage)
108 {
109 unsigned i, j;
110 wchar_t *cpname = NULL;
111 wchar_t data[1024];
112 DWORD dlen;
113 HKEY key;
114 static wchar_t font[1024];
115 DWORD type;
116
117 *codepage = 0;
118
119 /* Search the table for a match. */
120 for (i = 0; codepages[i].name != NULL; i++) {
121 if (!strcmp(cset, codepages[i].name)) {
122 cpname = codepages[i].codepage;
123 break;
124 }
125 }
126
127 /* If no match, use Lucida Console. */
128 if (cpname == NULL) {
129 return L"Lucida Console";
130 }
131
132 /*
133 * Look in the registry for the console font associated with the
134 * Windows code page.
135 */
136 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
137 "Software\\Microsoft\\Windows NT\\CurrentVersion\\"
138 "Console\\TrueTypeFont",
139 0,
140 KEY_READ,
141 &key) != ERROR_SUCCESS) {
142 printf("RegOpenKey failed -- cannot find font\n");
143 return L"Lucida Console";
144 }
145 dlen = sizeof(data);
146 if (RegQueryValueExW(key, cpname, NULL, &type, (LPVOID)data,
147 &dlen) != ERROR_SUCCESS) {
148 /* No codepage-specific match, try the default. */
149 dlen = sizeof(data);
150 if (RegQueryValueExW(key, L"0", NULL, &type, (LPVOID)data,
151 &dlen) != ERROR_SUCCESS) {
152 RegCloseKey(key);
153 printf("RegQueryValueEx failed -- cannot find font\n");
154 return L"Lucida Console";
155 }
156 }
157 RegCloseKey(key);
158 if (type == REG_MULTI_SZ) {
159 for (i = 0; i < dlen/sizeof(wchar_t); i++) {
160 if (data[i] == 0x0000) {
161 break;
162 }
163 }
164 if (i + 1 >= dlen / sizeof(wchar_t) || data[i + 1] == 0x0000) {
165 printf("Bad registry value -- cannot find font\n");
166 return L"Lucida Console";
167 }
168 i++;
169 } else {
170 i = 0;
171 }
172 for (j = 0; i < dlen; i++, j++) {
173 if (j == 0 && data[i] == L'*') {
174 i++;
175 }
176 if ((font[j] = data[i]) == 0x0000) {
177 break;
178 }
179 }
180 *codepage = _wtoi(cpname);
181 return font;
182 }
183
184 /* Convert a hexadecimal digit to a nybble. */
185 static unsigned
hex(char c)186 hex(char c)
187 {
188 static char *digits = "0123456789abcdef";
189 char *pos;
190
191 pos = strchr(digits, c);
192 if (pos == NULL) {
193 return 0; /* XXX */
194 }
195 return (unsigned)(pos - digits);
196 }
197
198 //#define DEBUG_EDIT 1
199
200 int
read_user_settings(FILE * f,char ** usp)201 read_user_settings(FILE *f, char **usp)
202 {
203 int saw_star;
204 char buf[1024];
205
206 if (usp == NULL) {
207 return 1; /* success */
208 }
209 *usp = NULL;
210
211 /*
212 * Read the balance of the file into a temporary buffer, ignoring
213 * the '!*' line.
214 */
215 saw_star = 0;
216 while (fgets(buf, sizeof(buf), f) != NULL) {
217 if (!saw_star) {
218 if (buf[0] == '!' && buf[1] == '*') {
219 saw_star = 1;
220 }
221 continue;
222 }
223 if (*usp == NULL) {
224 *usp = malloc(strlen(buf) + 1);
225 (*usp)[0] = '\0';
226 } else {
227 *usp = realloc(*usp, strlen(*usp) + strlen(buf) + 1);
228 }
229 if (*usp == NULL) {
230 #if defined(DEBUG_EDIT) /*[*/
231 printf("out of memory]\n");
232 #endif /*]*/
233 return 0;
234 }
235 strcat(*usp, buf);
236 }
237 return 1;
238 }
239
240 /*
241 * Read an existing session file.
242 * Returns 1 for success (file read and editable), 0 for failure.
243 */
244 int
read_session(FILE * f,session_t * s,char ** usp)245 read_session(FILE *f, session_t *s, char **usp)
246 {
247 char buf[1024];
248 int saw_hex = 0;
249 int saw_star = 0;
250 unsigned long csum;
251 unsigned long fcsum = 0;
252 int ver;
253 int s_off = 0;
254
255 /*
256 * Look for the checksum and version. Verify the version.
257 *
258 * XXX: It might be a good idea to validate each '!x' line and
259 * make sure that the length is right.
260 */
261 while (fgets(buf, sizeof(buf), f) != NULL) {
262 if (buf[0] == '!' && buf[1] == 'x')
263 saw_hex = 1;
264 else if (buf[0] == '!' && buf[1] == '*')
265 saw_star = 1;
266 else if (buf[0] == '!' && buf[1] == 'c') {
267 if (sscanf(buf + 2, "%lx %d", &csum, &ver) != 2) {
268 #if defined(DEBUG_EDIT) /*[*/
269 printf("[bad !c line '%s']\n", buf);
270 #endif /*]*/
271 return 0;
272 }
273 if (ver > WIZARD_VER) {
274 #if defined(DEBUG_EDIT) /*[*/
275 printf("[bad ver %d > %d]\n", ver, WIZARD_VER);
276 #endif /*]*/
277 return 0;
278 }
279 }
280 }
281 if (!saw_hex || !saw_star) {
282 #if defined(DEBUG_EDIT) /*[*/
283 printf("[missing%s%s]\n", saw_hex? "": "hex", saw_star? "": "star");
284 #endif /*]*/
285 return 0;
286 }
287
288 /* Checksum from the top up to the '!c' line. */
289 fflush(f);
290 fseek(f, 0, SEEK_SET);
291 fcsum = 0;
292 while (fgets(buf, sizeof(buf), f) != NULL) {
293 char *t;
294
295 if (buf[0] == '!' && buf[1] == 'c') {
296 break;
297 }
298
299 for (t = buf; *t; t++) {
300 fcsum += *t & 0xff;
301 }
302 }
303 if (fcsum != csum) {
304 #if defined(DEBUG_EDIT) /*[*/
305 printf("[checksum mismatch, want 0x%08lx got 0x%08lx]\n", csum, fcsum);
306 #endif /*]*/
307 return 0;
308 }
309
310 /* Once more, with feeling. Scribble onto the session structure. */
311 fflush(f);
312 fseek(f, 0, SEEK_SET);
313 s_off = 0;
314 while (fgets(buf, sizeof(buf), f) != NULL) {
315
316 if (buf[0] == '!' && buf[1] == 'x') {
317 char *t;
318
319 for (t = buf + 2; *t; t += 2) {
320 if (*t == '\n') {
321 break;
322 }
323 if (s_off > sizeof(*s)) {
324 #if defined(DEBUG_EDIT) /*[*/
325 printf("[s overflow: %d > %d]\n", s_off, (int)sizeof(*s));
326 #endif /*]*/
327 return 0;
328 }
329 ((char *)s)[s_off++] = (hex(*t) << 4) | hex(*(t + 1));
330 }
331 } else if (buf[0] == '!' && buf[1] == 'c') {
332 break;
333 }
334 }
335
336 /*
337 * Read the balance of the file into a temporary buffer, ignoring
338 * the '!*' line.
339 */
340 if (usp != NULL && read_user_settings(f, usp) == 0) {
341 return 0;
342 }
343
344 /* Success */
345 return 1;
346 }
347
348 HRESULT
create_shortcut(session_t * session,char * exepath,char * linkpath,char * args,char * workingdir)349 create_shortcut(session_t *session, char *exepath, char *linkpath, char *args,
350 char *workingdir)
351 {
352 wchar_t *font;
353 int codepage = 0;
354 int extra_height = 1;
355
356 font = reg_font_from_cset(session->codepage, &codepage);
357
358 if (!(session->flags & WF_NO_MENUBAR)) {
359 extra_height += 2;
360 }
361
362 return create_link(
363 exepath, /* path to executable */
364 linkpath, /* where to put the link */
365 "wc3270 session", /* description */
366 args, /* arguments */
367 workingdir, /* working directory */
368 (session->ov_rows? session->ov_rows: wrows[session->model])
369 + extra_height, /* console rows */
370 session->ov_cols? session->ov_cols: wcols[session->model],
371 /* console columns */
372 font, /* font */
373 session->point_size,/* point size */
374 codepage); /* code page */
375 }
376