update
[midnight-commander.git] / pc / util_os2.c
bloba6d64f162e1a9d3c08a0e9232a2893ffb3bf1825
1 /* Various utilities - OS/2 versions
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
4 Written 1994, 1995, 1996 by:
5 Juan Grigera, Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
6 Jakub Jelinek, Mauricio Plaza.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include <config.h>
24 #define INCL_DOS
25 #define INCL_PM
26 #define INCL_DOSPROCESS
27 #define INCL_DOSFILEMGR
28 #define INCL_DOSDEVICES /* Device values */
29 #define INCL_DOSDATETIME
30 #define INCL_DOSERRORS
31 #include <os2.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <ctype.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <io.h>
40 #include <fcntl.h>
41 #include <signal.h> /* my_system */
42 #include <limits.h> /* INT_MAX */
43 #include <sys/time.h> /* select: timeout */
44 #include <sys/param.h>
45 #include <sys/stat.h>
46 #include <stdarg.h>
47 #include <process.h>
48 #include "../src/fs.h"
49 #include "../src/util.h"
50 #include "../src/dialog.h"
52 #ifdef get_default_editor
53 #undef get_default_editor
54 #endif
56 char *get_default_shell();
59 #ifndef ENOTEMPTY
60 #define ENOTEMPTY ERROR_DIR_NOT_EMPTY
61 #endif
63 char *
64 get_owner (int uid)
66 return "none";
69 char *
70 get_group (int gid)
72 return "none";
75 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
76 /* More than that would be unportable */
77 #define MAX_PIPE_SIZE 4096
79 static int error_pipe[2]; /* File descriptors of error pipe */
80 static int old_error; /* File descriptor of old standard error */
82 /* Creates a pipe to hold standard error for a later analysis. */
83 /* The pipe can hold 4096 bytes. Make sure no more is written */
84 /* or a deadlock might occur. */
85 void
86 open_error_pipe (void)
88 return;
91 void
92 close_error_pipe (int error, char *text)
94 return;
97 void
98 check_error_pipe (void)
100 char error[MAX_PIPE_SIZE];
101 int len = 0;
102 if (old_error >= 0){
103 while (len < MAX_PIPE_SIZE)
105 int rvalue;
107 rvalue = read (error_pipe[0], error + len, 1);
108 len ++;
109 if (rvalue <= 0)
110 break;
112 error[len] = 0;
113 close (error_pipe[0]);
115 if (len > 0)
116 message (0, " Warning ", error);
120 static int
121 StartWindowsProg (char *name, SHORT type)
123 #if 0 /* FIXME: PM DDL's should be loaded (or not loaded) at run time */
124 PROGDETAILS pDetails;
126 memset(&pDetails, 0, sizeof(PROGDETAILS)) ;
127 pDetails.Length = sizeof(pDetails);
128 pDetails.pszExecutable = name; /* program name */
129 pDetails.pszStartupDir = NULL; /* default directory for new app. */
130 pDetails.pszParameters = NULL; /* command line */
131 pDetails.progt.fbVisible = SHE_VISIBLE ;
132 pDetails.pszEnvironment = NULL;
134 switch (type) {
135 case 0:
136 /* Win Standard */
137 pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
138 break;
139 case 1:
140 /* Win 3.1 Protect */
141 pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
142 break;
143 case 2:
144 /* Win 3.1 Enh. Protect */
145 pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
146 break;
147 default:
148 pDetails.progt.progc = PROG_31_ENHSEAMLESSCOMMON ;
149 break;
151 WinStartApp(NULLHANDLE,
152 &pDetails,
153 NULL,
154 NULL,
155 SAF_INSTALLEDCMDLINE|SAF_STARTCHILDAPP) ;
156 #endif
157 return 0;
161 static int
162 os2_system (int as_shell_command, const char *shell, const char *command, char *parm);
165 as_shell_command = 1: If a program is started during input line, CTRL-O
166 or RETURN
167 = 0: F3, F4
169 int
170 my_system (int as_shell_command, const char *shell, const char *command)
172 char *sh; /* This is the shell -- always! */
173 char *cmd; /* This is the command (only the command) */
174 char *parm; /* This is the parameter (can be more than one) */
175 register int length, i;
176 char temp[4096]; /* That's enough! */
177 char *t, *t1;
179 sh = get_default_shell();
180 if (!strcmp("/bin/sh", shell)) { /* Translate "/bin/sh" to "sh" */
181 return spawnlp(P_WAIT, "sh", shell, "-c", command, NULL);
182 } else if (( (t = strrchr(shell,'/')) /* Specialcase Bourne */
183 || (t = strrchr(shell,'\\')))
184 && (t1 = strchr(t, '.'))
185 && (
186 ((t1 - t == 4) && strncmp("bash", t, 4))
187 || ((t1 - t == 3) && strncmp("ksh", t, 3))
188 || ((t1 - t == 2) && strncmp("sh", t, 2))
189 )) {
190 return spawnlp(P_WAIT, shell, shell, "-c", command, NULL);
191 } else if (strcmp(sh, shell)) {
193 Not equal -- That means: shell is the program and command is the
194 parameter
196 cmd = (char *) shell;
197 parm = (char *) command;
198 } else {
199 /* look into the command and take out the program */
200 if (command) {
201 strcpy(temp, command);
202 length = strlen(command);
203 for (i=length-1; i>=0; i--) {
204 if (command[i] == ' ') {
205 temp[i] = (char) 0;
206 length--;
207 } else
208 break;
210 if (i==-1) {
211 /* only blanks */
212 return -1;
214 if (parm = strchr(temp, (char) ' ')) {
215 *parm = (char) 0;
216 parm++;
218 cmd = (char *) temp;
219 } else {
220 /* command is NULL */
221 cmd = parm = NULL;
224 return os2_system (as_shell_command, sh, cmd, parm);
227 static int
228 ux_startp (const char *shell, const char *command, const char *parm)
230 if (parm) {
231 spawnlp (P_WAIT,
232 (char *) shell,
233 (char *) shell,
234 "/c",
235 (char *) command,
236 (char *) parm,
237 (char *) 0);
238 } else {
239 spawnlp (P_WAIT,
240 (char *) shell,
241 (char *) shell,
242 "/c",
243 (char *) command,
244 (char *) 0);
246 return 0;
250 static int
251 os2_system (int as_shell_command, const char *shell, const char *command, char *parm)
253 register int i, j;
254 ULONG AppType = 0; /* Application type flags (returned) */
255 APIRET rc = NO_ERROR; /* Return Code */
256 char pathValue[5] = "PATH"; /* For DosSearchPath */
257 UCHAR searchResult[MC_MAXPATHLEN * 2 + 1]; /* For DosSearchPath */
259 char *cmdString;
260 char *postFix[3];
261 char *line;
262 /* ------------------------------------------------------- */
263 STARTDATA StartData;
264 CHAR ObjBuf[100];
265 ULONG SessionID;
266 PID pid;
268 if (command == NULL) {
269 /* .ado: just start a shell, we don't need the parameter */
270 spawnl (P_WAIT,
271 (char *) shell,
272 (char *) shell,
273 (char *) command, (char *) 0);
274 return 0;
277 memset(&StartData, 0, sizeof(StartData)) ;
278 StartData.Length = sizeof(StartData);
279 StartData.Related = SSF_RELATED_CHILD;
280 StartData.FgBg = SSF_FGBG_BACK;
281 StartData.TraceOpt = SSF_TRACEOPT_NONE;
282 StartData.PgmTitle = NULL;
283 StartData.TermQ = NULL;
284 StartData.InheritOpt = SSF_INHERTOPT_PARENT;
285 StartData.IconFile = 0;
286 StartData.PgmHandle = 0;
287 StartData.PgmControl = SSF_CONTROL_VISIBLE ;
288 StartData.ObjectBuffer = ObjBuf;
289 StartData.ObjectBuffLen = 100;
290 StartData.PgmInputs = parm;
292 postFix[0] = ".exe";
293 postFix[1] = ".cmd";
294 postFix[2] = ".bat";
296 i = strlen(command);
297 if (command[i-1] == ' ') {
298 /* The user has used ALT-RETURN */
299 i--;
301 cmdString = (char *) malloc(i+1);
302 for (j=0; j<i; j++) {
303 cmdString[j] = command[j];
305 cmdString[j] = (char) 0;
307 if ((i < 5) || ((i > 4) && (cmdString[i-4]) != '.')) {
308 /* without Extension */
309 line = (char *) malloc(i+5);
310 rc = 1;
311 for (i=0; (i<3 && rc); i++) {
312 /* Search for the file */
313 strcpy(line, cmdString);
314 strcat(line, postFix[i]);
315 rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
316 (PSZ) pathValue,
317 line,
318 searchResult,
319 sizeof(searchResult));
321 free (line);
322 } else {
323 /* Just search */
324 rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
325 (PSZ) pathValue,
326 cmdString,
327 searchResult,
328 sizeof(searchResult));
330 free(cmdString);
331 if (rc != 0) {
332 /* Internal command or the program was written with absolut path */
333 return ux_startp(shell, command, parm);
336 /* Application to be started */
337 StartData.PgmName = searchResult;
338 StartData.Environment = NULL;
339 rc = DosQueryAppType(searchResult, &AppType);
340 if (rc == NO_ERROR) {
341 StartData.SessionType = PROG_WINDOWABLEVIO;
342 if ((AppType & 0x00000007) == FAPPTYP_WINDOWAPI) {
343 /* Window API */
344 StartData.SessionType = PROG_PM;
345 return DosStartSession(&StartData, &SessionID, &pid);
347 if ((AppType & 0x00000007) == FAPPTYP_WINDOWCOMPAT) {
348 /* Window compat */
349 return ux_startp(shell, command, parm);
351 if (AppType & 0x0000ffff & FAPPTYP_DOS) {
352 /* PC/DOS Format */
353 StartData.SessionType = PROG_WINDOWEDVDM;
354 return DosStartSession(&StartData, &SessionID, &pid);
356 if (AppType & 0x0000ffff & FAPPTYP_WINDOWSREAL) {
357 /* Windows real mode app */
358 return StartWindowsProg(searchResult, 0);
360 if (AppType & 0x0000ffff & FAPPTYP_WINDOWSPROT) {
361 /* Windows Protect mode app*/
362 return StartWindowsProg(searchResult, 1);
364 if (AppType & 0x0000ffff & FAPPTYP_WINDOWSPROT31) {
365 /* Windows 3.1 Protect mode app*/
366 return StartWindowsProg(searchResult, 2);
368 rc = DosStartSession(&StartData, &SessionID, &pid) ;
369 } else {
370 /* It's not a known exe type or it's a CMD/BAT file */
371 i = strlen(searchResult);
372 if ((toupper(searchResult[--i]) == 'T') &&
373 (toupper(searchResult[--i]) == 'A') &&
374 (toupper(searchResult[--i]) == 'B') &&
375 (searchResult[--i] == '.') ) {
376 StartData.SessionType = PROG_WINDOWEDVDM;
377 rc = DosStartSession(&StartData, &SessionID, &pid) ;
378 } else {
379 rc = ux_startp (shell, command, parm);
382 return rc;
385 char *tilde_expand (char *directory)
387 return strdup (directory);
391 /* Canonicalize path, and return a new path. Do everything in situ.
392 The new path differs from path in:
393 Multiple BACKSLASHs are collapsed to a single BACKSLASH.
394 Leading `./'s and trailing `/.'s are removed.
395 Trailing BACKSLASHs are removed.
396 Non-leading `../'s and trailing `..'s are handled by removing
397 portions of the path. */
398 char *
399 canonicalize_pathname (char *path)
401 int i, start;
402 char stub_char;
404 stub_char = (*path == PATH_SEP) ? PATH_SEP : '.';
406 /* Walk along path looking for things to compact. */
407 i = 0;
408 for (;;) {
409 if (!path[i])
410 break;
412 while (path[i] && path[i] != PATH_SEP)
413 i++;
415 start = i++;
417 /* If we didn't find any slashes, then there is nothing left to do. */
418 if (!path[start])
419 break;
421 /* Handle multiple BACKSLASHs in a row. */
422 while (path[i] == PATH_SEP)
423 i++;
425 if ((start + 1) != i) {
426 strcpy (path + start + 1, path + i);
427 i = start + 1;
430 /* Handle backquoted BACKSLASH. */
431 /* if (start > 0 && path[start - 1] == '\\')
432 continue; */
434 /* Check for trailing BACKSLASH. */
435 if (start && !path[i]) {
436 zero_last:
437 path[--i] = '\0';
438 break;
441 /* Check for `../', `./' or trailing `.' by itself. */
442 if (path[i] == '.') {
443 /* Handle trailing `.' by itself. */
444 if (!path[i + 1])
445 goto zero_last;
447 /* Handle `./'. */
448 if (path[i + 1] == PATH_SEP) {
449 strcpy (path + i, path + i + 1);
450 i = start;
451 continue;
454 /* Handle `../' or trailing `..' by itself.
455 Remove the previous ?/ part with the exception of
456 ../, which we should leave intact. */
457 if (path[i + 1] == '.' && (path[i + 2] == PATH_SEP || !path[i + 2])) {
458 while (--start > -1 && path[start] != PATH_SEP);
459 if (!strncmp (path + start + 1, "..\\", 3))
460 continue;
461 strcpy (path + start + 1, path + i + 2);
462 i = start;
463 continue;
468 if (!*path) {
469 *path = stub_char;
470 path[1] = '\0';
472 return path;
476 void
477 my_statfs (struct my_statfs *myfs_stats, char *path)
479 PFSALLOCATE pBuf;
480 PFSINFO pFsInfo;
481 ULONG lghBuf;
483 ULONG diskNum = 0;
484 ULONG logical = 0;
486 UCHAR szDeviceName[3] = "A:";
487 PBYTE pszFSDName = NULL; /* pointer to FS name */
488 APIRET rc = NO_ERROR; /* Return code */
489 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
490 ULONG cbBuffer = sizeof(fsqBuffer); /* Buffer length) */
491 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2) fsqBuffer;
493 int i, len = 0;
495 /* ------------------------------------------------------------------ */
497 lghBuf = sizeof(FSALLOCATE);
498 pBuf = (PFSALLOCATE) malloc(lghBuf);
500 /* Get the free number of Bytes */
501 rc = DosQueryFSInfo(0L, FSIL_ALLOC, (PVOID) pBuf, lghBuf);
502 /* KBytes available */
503 myfs_stats->avail = pBuf->cSectorUnit * pBuf->cUnitAvail * pBuf->cbSector / 1024;
504 /* KBytes total */
505 myfs_stats->total = pBuf->cSectorUnit * pBuf->cUnit * pBuf->cbSector / 1024;
506 myfs_stats->nfree = pBuf->cUnitAvail;
507 myfs_stats->nodes = pBuf->cbSector;
509 lghBuf = sizeof(FSINFO);
510 pFsInfo = (PFSINFO) malloc(lghBuf);
511 rc = DosQueryFSInfo(0L,
512 FSIL_VOLSER,
513 (PVOID) pFsInfo,
514 lghBuf);
515 /* Get name */
516 myfs_stats->device = strdup(pFsInfo->vol.szVolLabel); /* Label of the Disk */
518 /* Get the current disk for DosQueryFSAttach */
519 rc = DosQueryCurrentDisk(&diskNum, &logical);
521 szDeviceName[0] = (UCHAR) (diskNum + (ULONG) 'A' - 1);
522 /* Now get the type of the disk */
523 rc = DosQueryFSAttach(szDeviceName,
524 0L,
525 FSAIL_QUERYNAME,
526 pfsqBuffer,
527 &cbBuffer);
529 pszFSDName = pfsqBuffer->szName + pfsqBuffer->cbName + 1;
530 myfs_stats->mpoint = strdup(pszFSDName); /* FAT, HPFS ... */
532 myfs_stats->type = pBuf->idFileSystem;
533 /* What is about 3 ?*/
534 if (myfs_stats->type == 0) {
535 myfs_stats->typename = (char *) malloc(11);
536 strcpy(myfs_stats->typename, "Local Disk");
537 } else {
538 myfs_stats->typename = (char *) malloc(13);
539 strcpy(myfs_stats->typename, "Other Device");
542 free(pBuf);
543 free(pFsInfo);
546 #ifndef __EMX__
547 int
548 gettimeofday (struct timeval* tvp, void *p)
550 DATETIME pdt = {0};
551 if (p != NULL) /* what is "p"? */
552 return 0;
554 /* Since MC only calls this func from get_random_hint we return
555 * some value, not exactly the "correct" one
557 DosGetDateTime(&pdt);
558 tvp->tv_usec = (pdt.hours * 60 + pdt.minutes) * 60 + pdt.seconds;
559 /* Number of milliseconds since Windows started */
560 tvp->tv_sec = tvp->tv_usec * 1000 + pdt.hundredths * 10;
561 return 0;
563 #endif
565 /* FAKE functions */
567 int
568 look_for_exe(const char* pathname)
570 int j;
571 char *p;
572 int lgh = strlen(pathname);
574 if (lgh < 4) {
575 return 0;
576 } else {
577 p = (char *) pathname;
578 for (j=0; j<lgh-4; j++) {
579 p++;
581 if (!stricmp(p, ".exe") ||
582 !stricmp(p, ".bat") ||
583 !stricmp(p, ".com") ||
584 !stricmp(p, ".cmd")) {
585 return 1;
588 return 0;
591 int
592 lstat (const char* pathname, struct stat *buffer)
594 int rc = stat (pathname, buffer);
595 #ifdef __BORLANDC__
596 if (rc == 0) {
597 if (!(buffer->st_mode & S_IFDIR)) {
598 if (!look_for_exe(pathname)) {
599 buffer->st_mode &= !S_IXUSR & !S_IXGRP & !S_IXOTH;
603 #endif
604 return rc;
607 int
608 getuid ()
610 return 0;
613 int
614 getgid ()
616 return 0;
619 int
620 readlink (char* path, char* buf, int size)
622 return -1;
625 int
626 symlink (char *n1, char *n2)
628 return -1;
631 int
632 link (char *p1, char *p2)
634 return -1;
637 int
638 chown (char *path, int owner, int group)
640 return -1;
643 int
644 mknod (char *path, int mode, int dev)
646 return -1;
649 void
650 init_uid_gid_cache (void)
652 return;
655 int
656 mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
658 return -1;
661 int
662 mc_doublepclose (int pipe, pid_t pid)
664 return 0;
667 #ifndef HAVE_VFS
668 /*hacks to get it compile, remove these after vfs works */
669 char *
670 vfs_get_current_dir (void)
672 return NULL;
674 #endif
676 int
677 vfs_current_is_extfs (void)
679 return 0;
682 #ifndef HAVE_VFS
683 int
684 vfs_file_is_ftp (char *filename)
686 return 0;
690 mc_utime (char *path, void *times)
692 return 0;
696 void
697 extfs_run (char *file)
699 return;
701 #endif
703 void *
704 getgrent(void) { return NULL; }
706 void
707 setgrent(void) {}
709 void
710 endgrent(void) {}
713 setreuid(uid_t ruid, uid_t euid) { return -1; }
715 pid_t
716 setsid(void) { return (pid_t)-1; }
719 mkfifo(const char *path, mode_t mode) { return -1; }
721 int
722 socketpair(int i, int i1, int i2, int *i3)
724 return -1;
728 #ifndef HAVE_VFS
730 mc_chdir(char *pathname)
732 APIRET ret;
733 register int lgh = strlen(pathname);
735 /* Set the current drive */
736 if (lgh == 0) {
737 return -1;
738 } else {
739 /* First set the default drive */
740 if (lgh > 1) {
741 if (pathname[1] == ':') {
742 ret = DosSetDefaultDisk(toupper(pathname[0]) - 'A' + 1);
745 /* After that, set the current dir! */
746 ret = DosSetCurrentDir(pathname);
748 return ret;
752 mc_chmod(char *pathName, int unxmode)
754 /* OS/2 does not need S_REG */
755 int os2Mode = unxmode & 0x0FFF;
756 return chmod(pathName, os2Mode);
758 #endif
760 static int
761 conv_os2_unx_rc(int os2rc)
763 int errCode;
764 switch (os2rc) {
765 case ERROR_FILE_NOT_FOUND:
766 case ERROR_PATH_NOT_FOUND:
767 case ERROR_FILENAME_EXCED_RANGE:
768 errCode = ENOENT;
769 break;
770 case ERROR_NOT_DOS_DISK:
771 case ERROR_SHARING_VIOLATION:
772 case ERROR_SHARING_BUFFER_EXCEEDED:
773 case ERROR_ACCESS_DENIED:
774 errCode = EACCES;
775 break;
776 case ERROR_INVALID_PARAMETER:
777 errCode = EINVAL;
778 break;
779 default:
780 errCode = EINVAL;
781 break;
783 return errCode;
786 #ifndef HAVE_VFS
788 mc_open (char *file, int flags, int pmode)
790 return open(file, (flags | O_BINARY), pmode);
794 mc_unlink(char *pathName)
796 /* Use OS/2 API to delete a file, if the file is set as read-only,
797 the file will be deleted without asking the user! */
798 APIRET rc;
799 rc = DosDelete(pathName);
800 if (!rc) {
801 return 0;
803 if (rc == ERROR_ACCESS_DENIED) {
804 chmod(pathName, (S_IREAD|S_IWRITE));
805 rc = DosDelete(pathName);
806 if (rc) {
807 errno = conv_os2_unx_rc(rc) ;
808 return -1;
809 } else {
810 return 0;
812 } else {
813 errno = conv_os2_unx_rc(rc) ;
814 return -1;
817 #endif
819 char *
820 get_default_editor (void)
822 char *tmp;
823 APIRET rc;
824 char pathValue[5] = "PATH";
825 UCHAR searchResult[MC_MAXPATHLEN + 1];
827 /* EPM is not always be installed */
828 rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
829 (PSZ) pathValue,
830 "EPM.EXE",
831 searchResult,
832 sizeof(searchResult));
833 if (rc != 0) {
834 /* The system editor is always there */
835 return strdup("e.exe");
836 } else {
837 /* Let it be searched from my_system */
838 return strdup("epm.exe");
842 /* get_default_shell
843 Get the default shell for the current hardware platform
844 TODO: Get the value of %OS2_SHELL% or %SHELL%: which one?
846 char *
847 get_default_shell()
849 return getenv ("COMSPEC");
853 errno_dir_not_empty (int err)
855 if (err == ENOTEMPTY)
856 return 1;
857 return 0;
860 /* The MC library directory is by default the directory where mc.exe
861 is situated. It is recommended to specify this directory via MCHOME
862 environment variable, otherwise you will be unable to rename mc.exe */
863 char *
864 get_mc_lib_dir ()
866 HMODULE mc_hm;
867 int rc;
868 char *cur = NULL;
869 char *mchome = getenv("MCHOME");
871 if (mchome && *mchome)
872 return mchome;
873 mchome = malloc(MC_MAXPATHLEN);
874 rc = DosQueryModuleHandle ("MC.EXE", &mc_hm);
875 if (!rc)
876 rc = DosQueryModuleName (mc_hm, MC_MAXPATHLEN, mchome);
877 if (!rc)
879 for (cur = mchome + strlen(mchome); \
880 (cur > mchome) && (*cur != PATH_SEP); cur--);
881 *cur = 0;
882 cur = strdup(mchome);
883 free(mchome);
885 if (!cur || !*cur) {
886 free(cur);
887 return "C:\\MC";
889 return cur;
892 int get_user_rights (struct stat *buf)
894 return 2;
896 void init_groups (void)
899 void delete_groups (void)