1 |
#ifndef URLADER |
2 |
# define URLADER "urlader" |
3 |
#endif |
4 |
#define URLADER_VERSION "1.0" // a decimal number, not a version string |
5 |
|
6 |
#define MAX_ARGC 32 |
7 |
#define MAX_ARGS 256 |
8 |
|
9 |
#include <stdio.h> |
10 |
#include <stdlib.h> |
11 |
#include <unistd.h> |
12 |
#include <errno.h> |
13 |
#include <string.h> |
14 |
#include <time.h> |
15 |
#include <fcntl.h> |
16 |
|
17 |
#include "liblzf/lzf_d.c" |
18 |
|
19 |
#define TAIL_MAGIC "SCHMORPPACK0" |
20 |
|
21 |
#ifdef _WIN32 |
22 |
|
23 |
#include <windows.h> |
24 |
//#include <winbase.h> |
25 |
#include <shlobj.h> |
26 |
#include <shlwapi.h> |
27 |
#include <wininet.h> |
28 |
|
29 |
static DWORD dword; |
30 |
|
31 |
#define u_handle HANDLE |
32 |
#define u_invalid_handle 0 |
33 |
#define u_valid(handle) (!!handle) |
34 |
|
35 |
#define u_setenv(name,value) SetEnvironmentVariable (name, value) |
36 |
#define u_mkdir(path) !CreateDirectory (path, NULL) |
37 |
#define u_chdir(path) !SetCurrentDirectory (path) |
38 |
#define u_rename(fr,to) !MoveFile (fr, to) |
39 |
#define u_open(path) CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) |
40 |
#define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) |
41 |
#define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) |
42 |
#define u_close(handle) CloseHandle (handle) |
43 |
#define u_append(path,add) PathAppend (path, add) |
44 |
#define u_write(handle,data,len) (WriteFile (handle, data, len, &dword, 0) ? dword : -1) |
45 |
|
46 |
#define u_fsync(handle) FlushFileBuffers (handle) |
47 |
#define u_sync() |
48 |
|
49 |
#define u_lockfile(path) CreateFile (path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) |
50 |
#define u_cloexec(handle) |
51 |
// acquire shared in addition to excl, then unlock excl |
52 |
#define u_lock_write_to_read(wait) u_lock (U_LOCK_READ, 1); u_lock (U_UNLOCK, 1); |
53 |
|
54 |
#else |
55 |
|
56 |
#define _GNU_SOURCE 1 |
57 |
#define _BSD_SOURCE 1 |
58 |
// the above increases our chances of getting MAP_ANONYMOUS |
59 |
|
60 |
#include <sys/mman.h> |
61 |
#include <sys/types.h> |
62 |
#include <pwd.h> |
63 |
|
64 |
#if defined (MAP_ANON) && !defined (MAP_ANONYMOUS) |
65 |
#define MAP_ANONYMOUS MAP_ANON |
66 |
#endif |
67 |
|
68 |
#ifdef PATH_MAX |
69 |
#define MAX_PATH (PATH_MAX < 4096 ? 4096 : PATH_MAX) |
70 |
#else |
71 |
#define MAX_PATH 4096 |
72 |
#endif |
73 |
|
74 |
#define u_handle int |
75 |
#define u_invalid_handle -1 |
76 |
#define u_valid(fd) ((fd) >= 0) |
77 |
|
78 |
#define u_setenv(name,value) setenv (name, value, 1) |
79 |
#define u_mkdir(path) mkdir (path, 0777) |
80 |
#define u_chdir(path) chdir (path) |
81 |
#define u_rename(fr,to) rename (fr, to) |
82 |
#define u_open(path) open (path, O_RDONLY) |
83 |
#define u_creat(path,exec) open (path, O_WRONLY | O_CREAT | O_TRUNC, (exec) ? 0777 : 0666) |
84 |
#define u_close(handle) close (handle) |
85 |
#define u_append(path,add) strcat (strcat (path, "/"), add) |
86 |
#define u_write(handle,data,len) write (handle, data, len) |
87 |
|
88 |
// on a mostly idle system, a sync at the end is certainly faster, hope for the best |
89 |
#define u_fsync(handle) |
90 |
#define u_sync() sync () |
91 |
|
92 |
#define u_lockfile(path) open (path, O_RDWR | O_CREAT, 0666) |
93 |
#define u_cloexec(handle) fcntl (handle, F_SETFD, FD_CLOEXEC) |
94 |
#define u_lock_write_to_read(wait) u_lock (U_LOCK_READ, 1); |
95 |
|
96 |
#endif |
97 |
|
98 |
#define u_16(ptr) (((ptr)[0] << 8) | (ptr)[1]) |
99 |
#define u_32(ptr) (((ptr)[0] << 24) | ((ptr)[1] << 16) | ((ptr)[2] << 8) | (ptr)[3]) |
100 |
|
101 |
/* some simple? dynamic memory management for paths might save ltos of ram */ |
102 |
static u_handle pack_handle; |
103 |
static u_handle lock_handle; |
104 |
static char tmppath[MAX_PATH]; |
105 |
static char currdir[MAX_PATH]; |
106 |
static char datadir[MAX_PATH]; // %AppData%/urlader |
107 |
static char exe_dir[MAX_PATH]; // %AppData%/urlader/EXE_ID |
108 |
static char execdir[MAX_PATH]; // %AppData%/urlader/EXE_ID/EXE_VER |
109 |
static char exe_id[MAX_PATH]; |
110 |
static char exe_ver[MAX_PATH]; |
111 |
|
112 |
static int exe_argc; |
113 |
static const char *exe_argv[MAX_ARGC]; |
114 |
static char exe_args[MAX_ARGS]; /* actual arguments strings copied here */ |
115 |
static unsigned int exe_argo; |
116 |
|
117 |
static void |
118 |
fatal (const char *msg) |
119 |
{ |
120 |
#ifdef _WIN32 |
121 |
MessageBox (0, msg, URLADER, 0); |
122 |
#else |
123 |
write (2, URLADER ": ", sizeof (URLADER ": ") - 1); |
124 |
write (2, msg, strlen (msg)); |
125 |
write (2, "\n", 1); |
126 |
#endif |
127 |
|
128 |
_exit (1); |
129 |
} |
130 |
|
131 |
static void * |
132 |
u_malloc (unsigned int size) |
133 |
{ |
134 |
void *addr; |
135 |
|
136 |
#ifdef _WIN32 |
137 |
HANDLE handle = CreateFileMapping (0, 0, PAGE_READWRITE, 0, size, NULL); |
138 |
|
139 |
if (!handle) |
140 |
return 0; |
141 |
|
142 |
addr = MapViewOfFile (handle, FILE_MAP_WRITE, 0, 0, size); |
143 |
|
144 |
CloseHandle (handle); |
145 |
#elif defined (MAP_ANONYMOUS) |
146 |
addr = mmap (0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
147 |
|
148 |
if (addr == (void *)-1) |
149 |
addr = 0; |
150 |
#else |
151 |
addr = malloc (size); |
152 |
#endif |
153 |
|
154 |
return addr; |
155 |
} |
156 |
|
157 |
static void |
158 |
u_free (void *addr, unsigned int size) |
159 |
{ |
160 |
#ifdef _WIN32 |
161 |
UnmapViewOfFile (addr); |
162 |
#elif defined (MAP_ANONYMOUS) |
163 |
munmap (addr, size); |
164 |
#else |
165 |
free (addr); |
166 |
#endif |
167 |
} |
168 |
|
169 |
static void * |
170 |
u_mmap (u_handle h, unsigned int size) |
171 |
{ |
172 |
void *addr; |
173 |
|
174 |
#ifdef _WIN32 |
175 |
HANDLE handle = CreateFileMapping (h, 0, PAGE_READONLY, 0, size, NULL); |
176 |
|
177 |
if (!handle) |
178 |
return 0; |
179 |
|
180 |
addr = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, size); |
181 |
|
182 |
CloseHandle (handle); |
183 |
#else |
184 |
addr = mmap (0, size, PROT_READ, MAP_SHARED, h, 0); |
185 |
|
186 |
if (addr == (void *)-1) |
187 |
addr = 0; |
188 |
#endif |
189 |
|
190 |
return addr; |
191 |
} |
192 |
|
193 |
static void |
194 |
u_munmap (void *addr, unsigned int len) |
195 |
{ |
196 |
#ifdef _WIN32 |
197 |
UnmapViewOfFile (addr); |
198 |
#else |
199 |
munmap (addr, len); |
200 |
#endif |
201 |
} |
202 |
|
203 |
static void |
204 |
tmpdir (const char *dir) |
205 |
{ |
206 |
static int cnt; |
207 |
|
208 |
for (;;) |
209 |
{ |
210 |
// #ifdef _WIN32 |
211 |
// sprintf (tmppath, "%s/%x_%x.tmp", dir, (unsigned int)GetCurrentProcessId (), ++cnt); |
212 |
// #else |
213 |
sprintf (tmppath, "%s/t-%x_%x.tmp", dir, (unsigned int)getpid () , ++cnt); |
214 |
// #endif |
215 |
|
216 |
if (!u_mkdir (tmppath)) |
217 |
return; |
218 |
} |
219 |
} |
220 |
|
221 |
enum |
222 |
{ |
223 |
U_UNLOCK, |
224 |
U_LOCK_READ, |
225 |
U_LOCK_WRITE |
226 |
}; |
227 |
|
228 |
static int |
229 |
u_lock (int mode, int dowait) |
230 |
{ |
231 |
#ifdef _WIN32 |
232 |
if (mode == U_UNLOCK) |
233 |
return !UnlockFile (lock_handle, 0, 0, 1, 0); |
234 |
else |
235 |
{ |
236 |
OVERLAPPED ov = { 0 }; |
237 |
|
238 |
return !LockFileEx (lock_handle, |
239 |
0 |
240 |
| (mode == U_LOCK_WRITE ? LOCKFILE_EXCLUSIVE_LOCK : 0) |
241 |
| (dowait ? 0 : LOCKFILE_FAIL_IMMEDIATELY), |
242 |
0, |
243 |
1, 0, |
244 |
&ov); |
245 |
} |
246 |
#else |
247 |
static short mode2lck [] = { F_UNLCK, F_RDLCK, F_WRLCK }; |
248 |
struct flock lck; |
249 |
lck.l_type = mode2lck [mode]; |
250 |
lck.l_whence = SEEK_SET; |
251 |
lck.l_start = 0; |
252 |
lck.l_len = 1; |
253 |
|
254 |
return fcntl (lock_handle, dowait ? F_SETLKW : F_SETLK, &lck); |
255 |
#endif |
256 |
} |
257 |
|
258 |
static void |
259 |
systemv (const char *const argv[]) |
260 |
{ |
261 |
#ifdef _WIN32 |
262 |
_spawnv (P_WAIT, argv [0], argv); |
263 |
#else |
264 |
pid_t pid = fork (); |
265 |
|
266 |
if (pid < 0) |
267 |
fatal ("fork failure"); |
268 |
|
269 |
if (!pid) |
270 |
{ |
271 |
execv (argv [0], (void *)argv); |
272 |
_exit (124); |
273 |
} |
274 |
|
275 |
int status; |
276 |
waitpid (pid, &status, 0); |
277 |
#endif |
278 |
} |
279 |
|
280 |
static void |
281 |
deltree (const char *path) |
282 |
{ |
283 |
#ifdef _WIN32 |
284 |
char buf[MAX_PATH * 2 + 64]; |
285 |
const char *argv[] = { getenv ("COMSPEC"), "/c", "rd", "/s", "/q", buf, 0 }; |
286 |
sprintf (buf, "\"%s\"", path); |
287 |
#else |
288 |
const char *argv[] = { "/bin/rm", "-rf", path, 0 }; |
289 |
#endif |
290 |
systemv (argv); |
291 |
} |
292 |
|
293 |
enum |
294 |
{ |
295 |
T_NULL, // 5 |
296 |
T_META, // 1 : exe_id, exe_ver |
297 |
T_ENV, // 2 : name, value |
298 |
T_ARG, // 3 : arg |
299 |
T_DIR, // 4+: path |
300 |
T_FILE, // 4+: path, data |
301 |
T_NUM |
302 |
}; |
303 |
|
304 |
enum |
305 |
{ |
306 |
F_EXEC = 1, |
307 |
F_LZF = 2, |
308 |
F_NULL = 0 |
309 |
}; |
310 |
|
311 |
struct hdr |
312 |
{ |
313 |
unsigned char type; |
314 |
unsigned char flags; |
315 |
unsigned char namelen[2]; |
316 |
unsigned char datalen[4]; |
317 |
}; |
318 |
|
319 |
struct tail { |
320 |
unsigned char max_filesize[4]; |
321 |
unsigned char size[4]; |
322 |
char magic[12]; |
323 |
}; |
324 |
|
325 |
static char *pack_base, *pack_end; |
326 |
static struct hdr *pack_cur; |
327 |
static char *scratch; |
328 |
static unsigned int scratch_size; |
329 |
|
330 |
#define PACK_NAME ((char *)(pack_cur + 1)) |
331 |
#define PACK_DATA (PACK_NAME + u_16 (pack_cur->namelen) + 1) |
332 |
#define PACK_VALID pack_cur->type |
333 |
|
334 |
static void |
335 |
pack_next (void) |
336 |
{ |
337 |
unsigned int d = u_32 (pack_cur->datalen); |
338 |
|
339 |
pack_cur = (struct hdr *)(PACK_DATA + d); |
340 |
} |
341 |
|
342 |
static void |
343 |
pack_unmap (void) |
344 |
{ |
345 |
if (pack_base) |
346 |
{ |
347 |
u_munmap (pack_base, pack_end - pack_base); |
348 |
pack_base = 0; |
349 |
} |
350 |
|
351 |
if (scratch) |
352 |
{ |
353 |
u_free (scratch, scratch_size); |
354 |
scratch = 0; |
355 |
} |
356 |
} |
357 |
|
358 |
static int |
359 |
pack_map (void) |
360 |
{ |
361 |
char *addr; |
362 |
unsigned int size; |
363 |
|
364 |
#ifdef _WIN32 |
365 |
BY_HANDLE_FILE_INFORMATION fi; |
366 |
|
367 |
if (!GetFileInformationByHandle (pack_handle, &fi)) |
368 |
return 0; |
369 |
|
370 |
size = fi.nFileSizeLow; |
371 |
#else |
372 |
size = lseek (pack_handle, 0, SEEK_END); |
373 |
#endif |
374 |
|
375 |
addr = u_mmap (pack_handle, size); |
376 |
if (!addr) |
377 |
return 0; |
378 |
|
379 |
pack_unmap (); |
380 |
|
381 |
pack_base = addr; |
382 |
pack_end = pack_base + size; |
383 |
|
384 |
struct tail *tail; |
385 |
|
386 |
tail = (void *)(pack_end - sizeof (*tail)); |
387 |
|
388 |
if (memcmp (tail->magic, TAIL_MAGIC, sizeof (TAIL_MAGIC) - 1)) |
389 |
return 0; |
390 |
|
391 |
pack_cur = (struct hdr *)(pack_end - u_32 (tail->size)); |
392 |
|
393 |
scratch = u_malloc (scratch_size = u_32 (tail->max_filesize)); |
394 |
if (!scratch) |
395 |
return 0; |
396 |
|
397 |
return 1; |
398 |
} |
399 |
|
400 |
static void |
401 |
exe_info (void) |
402 |
{ |
403 |
if (!pack_map ()) |
404 |
fatal ("unable to locate packfile in executable - executable corrupted?"); |
405 |
|
406 |
if (pack_cur->type != T_META) |
407 |
fatal ("unable to locate executable metadata - executable corrupted?"); |
408 |
|
409 |
strcpy (exe_id, PACK_NAME); |
410 |
} |
411 |
|
412 |
static void |
413 |
load (void) |
414 |
{ |
415 |
u_mkdir (datadir); |
416 |
|
417 |
strcpy (exe_dir, datadir); |
418 |
u_append (exe_dir, exe_id); |
419 |
u_mkdir (exe_dir); |
420 |
|
421 |
if (u_chdir (exe_dir)) |
422 |
fatal ("unable to change to application data directory"); |
423 |
|
424 |
u_handle h = u_open ("override"); |
425 |
if (u_valid (h)) |
426 |
{ |
427 |
u_handle oh = pack_handle; |
428 |
|
429 |
pack_handle = h; |
430 |
if (!pack_map ()) |
431 |
{ |
432 |
pack_handle = oh; |
433 |
oh = h; |
434 |
} |
435 |
|
436 |
u_close (oh); |
437 |
} |
438 |
|
439 |
if (pack_cur->type != T_META) |
440 |
fatal ("unable to locate override metadata"); |
441 |
|
442 |
strcpy (exe_ver, PACK_DATA); |
443 |
pack_next (); |
444 |
|
445 |
for (;;) |
446 |
{ |
447 |
if (pack_cur->type == T_ENV) |
448 |
u_setenv (PACK_NAME, PACK_DATA); |
449 |
else if (pack_cur->type == T_ARG) |
450 |
{ |
451 |
int len = u_16 (pack_cur->namelen) + 1; |
452 |
exe_argv [exe_argc++] = exe_args + exe_argo; |
453 |
memcpy (exe_args + exe_argo, PACK_NAME, len); |
454 |
exe_argo += len; |
455 |
} |
456 |
else |
457 |
break; |
458 |
|
459 |
pack_next (); |
460 |
} |
461 |
|
462 |
done_env_arg: |
463 |
strcpy (execdir, exe_dir); |
464 |
u_append (execdir, "i-"); |
465 |
strcat (execdir, exe_ver); |
466 |
|
467 |
strcat (strcpy (tmppath, execdir), ".lck"); |
468 |
lock_handle = u_lockfile (tmppath); |
469 |
if (!lock_handle) |
470 |
fatal ("unable to create lock file"); |
471 |
|
472 |
u_cloexec (lock_handle); |
473 |
|
474 |
// all users that uee an instance acquire a shared lock |
475 |
// acquiring an exclusive lock is enough to delete the dir |
476 |
// deleeting the lockfile is another topic altogether |
477 |
u_lock (U_LOCK_READ, 1); |
478 |
|
479 |
if (access (execdir, F_OK)) |
480 |
{ |
481 |
// does not exist yet, so unpack and move |
482 |
tmpdir (exe_dir); |
483 |
|
484 |
if (u_chdir (tmppath)) |
485 |
fatal ("unable to change to new instance directory"); |
486 |
|
487 |
for (;;) |
488 |
{ |
489 |
switch (pack_cur->type) |
490 |
{ |
491 |
case T_DIR: |
492 |
u_mkdir (PACK_NAME); |
493 |
break; |
494 |
|
495 |
case T_FILE: |
496 |
{ |
497 |
u_handle h = u_creat (PACK_NAME, pack_cur->flags & F_EXEC); |
498 |
unsigned int dlen, len = u_32 (pack_cur->datalen); |
499 |
char *data = PACK_DATA; |
500 |
|
501 |
if (pack_cur->flags & F_LZF) |
502 |
if (dlen = lzf_decompress (data, len, scratch, scratch_size)) |
503 |
{ |
504 |
data = scratch; |
505 |
len = dlen; |
506 |
} |
507 |
else |
508 |
fatal ("unable to uncompress file data - pack corrupted?"); |
509 |
|
510 |
if (!u_valid (h)) |
511 |
fatal ("unable to unpack file from packfile - disk full?"); |
512 |
|
513 |
if (u_write (h, data, len) != len) |
514 |
fatal ("unable to unpack file from packfile - disk full?"); |
515 |
|
516 |
u_fsync (h); |
517 |
u_close (h); |
518 |
} |
519 |
break; |
520 |
|
521 |
case T_NULL: |
522 |
goto done; |
523 |
} |
524 |
|
525 |
pack_next (); |
526 |
} |
527 |
|
528 |
done: |
529 |
if (u_chdir (datadir)) |
530 |
fatal ("unable to change to data directory"); |
531 |
|
532 |
u_sync (); |
533 |
|
534 |
if (u_rename (tmppath, execdir)) |
535 |
deltree (tmppath); // if move fails, delete new, assume other process created it independently |
536 |
} |
537 |
|
538 |
pack_unmap (); |
539 |
u_close (pack_handle); |
540 |
|
541 |
if (u_chdir (execdir)) |
542 |
fatal ("unable to change to application instance directory"); |
543 |
|
544 |
u_setenv ("URLADER_VERSION", URLADER_VERSION); |
545 |
u_setenv ("URLADER_DATADIR", datadir); |
546 |
u_setenv ("URLADER_EXECDIR", execdir); |
547 |
u_setenv ("URLADER_EXE_ID" , exe_id); |
548 |
u_setenv ("URLADER_EXE_DIR", exe_dir); |
549 |
u_setenv ("URLADER_EXE_VER", exe_ver); |
550 |
|
551 |
#if 0 |
552 |
// yes, this is overkill |
553 |
u_setenv ("SHLIB_PATH" , execdir); // hpux |
554 |
u_setenv ("LIBPATH" , execdir); // aix |
555 |
u_setenv ("LD_LIBRARY_PATH" , execdir); // most elf systems |
556 |
u_setenv ("LD_LIBRARY_PATH_32", execdir); // solaris |
557 |
u_setenv ("LD_LIBRARY_PATH_64", execdir); // solaris |
558 |
u_setenv ("LD_LIBRARYN32_PATH", execdir); // irix |
559 |
u_setenv ("LD_LIBRARY64_PATH" , execdir); // irix |
560 |
u_setenv ("DYLD_LIBRARY_PATH" , execdir); // os sucks from apple |
561 |
#endif |
562 |
} |
563 |
|
564 |
static void |
565 |
execute (void) |
566 |
{ |
567 |
exe_argv [exe_argc] = 0; |
568 |
systemv (exe_argv); |
569 |
} |
570 |
|
571 |
#ifdef _WIN32 |
572 |
|
573 |
int APIENTRY |
574 |
WinMain (HINSTANCE hI, HINSTANCE hP, LPSTR argv, int command_show) |
575 |
{ |
576 |
if (!GetModuleFileName (hI, tmppath, sizeof (tmppath))) |
577 |
fatal ("unable to find executable pack"); |
578 |
|
579 |
pack_handle = u_open (tmppath); |
580 |
if (!u_valid (pack_handle)) |
581 |
fatal ("unable to open executable pack"); |
582 |
|
583 |
exe_info (); |
584 |
|
585 |
if (!GetCurrentDirectory (sizeof (currdir), currdir)) |
586 |
strcpy (currdir, "."); |
587 |
|
588 |
if (SHGetFolderPath (0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, datadir) != S_OK) |
589 |
fatal ("unable to find application data directory"); |
590 |
|
591 |
u_mkdir (datadir); |
592 |
u_append (datadir, URLADER); |
593 |
|
594 |
load (); |
595 |
execute (); |
596 |
|
597 |
return 0; |
598 |
} |
599 |
|
600 |
#else |
601 |
|
602 |
int |
603 |
main (int argc, char *argv[]) |
604 |
{ |
605 |
char *home = getenv ("HOME"); |
606 |
|
607 |
pack_handle = u_open (argv [0]); |
608 |
if (!u_valid (pack_handle)) |
609 |
fatal ("unable to open executable pack"); |
610 |
|
611 |
exe_info (); |
612 |
|
613 |
if (!home) |
614 |
{ |
615 |
struct passwd *pw; |
616 |
|
617 |
if ((pw = getpwuid (getuid ()))) |
618 |
home = pw->pw_dir; |
619 |
else |
620 |
home = "/tmp"; |
621 |
} |
622 |
|
623 |
if (!getcwd (currdir, sizeof (currdir))) |
624 |
strcpy (currdir, "."); |
625 |
|
626 |
u_mkdir (home); |
627 |
//strcat (strcat (strcpy (datadir, home), "/."), URLADER); |
628 |
sprintf (datadir, "%s/.%s", home, URLADER); |
629 |
u_mkdir (datadir); |
630 |
|
631 |
#if 0 |
632 |
if (gethostname (tmppath, sizeof (tmppath))) |
633 |
strcpy (tmppath, "default"); |
634 |
|
635 |
u_append (datadir, tmppath); |
636 |
#endif |
637 |
|
638 |
load (); |
639 |
execute (); |
640 |
|
641 |
return 0; |
642 |
} |
643 |
|
644 |
#endif |
645 |
|