*** empty log message ***
[midnight-commander.git] / pc / util_os2.c
blob378dc6c375f2f5ae4dab0af027f5dabba66b19d2
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 /* Check for trailing BACKSLASH. */
431 if (start && !path[i]) {
432 zero_last:
433 path[--i] = '\0';
434 break;
437 /* Check for `../', `./' or trailing `.' by itself. */
438 if (path[i] == '.') {
439 /* Handle trailing `.' by itself. */
440 if (!path[i + 1])
441 goto zero_last;
443 /* Handle `./'. */
444 if (path[i + 1] == PATH_SEP) {
445 strcpy (path + i, path + i + 1);
446 i = start;
447 continue;
450 /* Handle `../' or trailing `..' by itself.
451 Remove the previous ?/ part with the exception of
452 ../, which we should leave intact. */
453 if (path[i + 1] == '.' && (path[i + 2] == PATH_SEP || !path[i + 2])) {
454 while (--start > -1 && path[start] != PATH_SEP);
455 if (!strncmp (path + start + 1, "..\\", 3))
456 continue;
457 strcpy (path + start + 1, path + i + 2);
458 i = start;
459 continue;
464 if (!*path) {
465 *path = stub_char;
466 path[1] = '\0';
468 return path;
472 void
473 my_statfs (struct my_statfs *myfs_stats, char *path)
475 PFSALLOCATE pBuf;
476 PFSINFO pFsInfo;
477 ULONG lghBuf;
479 ULONG diskNum = 0;
480 ULONG logical = 0;
482 UCHAR szDeviceName[3] = "A:";
483 PBYTE pszFSDName = NULL; /* pointer to FS name */
484 APIRET rc = NO_ERROR; /* Return code */
485 BYTE fsqBuffer[sizeof(FSQBUFFER2) + (3 * CCHMAXPATH)] = {0};
486 ULONG cbBuffer = sizeof(fsqBuffer); /* Buffer length) */
487 PFSQBUFFER2 pfsqBuffer = (PFSQBUFFER2) fsqBuffer;
489 int i, len = 0;
491 /* ------------------------------------------------------------------ */
493 lghBuf = sizeof(FSALLOCATE);
494 pBuf = (PFSALLOCATE) malloc(lghBuf);
496 /* Get the free number of Bytes */
497 rc = DosQueryFSInfo(0L, FSIL_ALLOC, (PVOID) pBuf, lghBuf);
498 /* KBytes available */
499 myfs_stats->avail = pBuf->cSectorUnit * pBuf->cUnitAvail * pBuf->cbSector / 1024;
500 /* KBytes total */
501 myfs_stats->total = pBuf->cSectorUnit * pBuf->cUnit * pBuf->cbSector / 1024;
502 myfs_stats->nfree = pBuf->cUnitAvail;
503 myfs_stats->nodes = pBuf->cbSector;
505 lghBuf = sizeof(FSINFO);
506 pFsInfo = (PFSINFO) malloc(lghBuf);
507 rc = DosQueryFSInfo(0L,
508 FSIL_VOLSER,
509 (PVOID) pFsInfo,
510 lghBuf);
511 /* Get name */
512 myfs_stats->device = strdup(pFsInfo->vol.szVolLabel); /* Label of the Disk */
514 /* Get the current disk for DosQueryFSAttach */
515 rc = DosQueryCurrentDisk(&diskNum, &logical);
517 szDeviceName[0] = (UCHAR) (diskNum + (ULONG) 'A' - 1);
518 /* Now get the type of the disk */
519 rc = DosQueryFSAttach(szDeviceName,
520 0L,
521 FSAIL_QUERYNAME,
522 pfsqBuffer,
523 &cbBuffer);
525 pszFSDName = pfsqBuffer->szName + pfsqBuffer->cbName + 1;
526 myfs_stats->mpoint = strdup(pszFSDName); /* FAT, HPFS ... */
528 myfs_stats->type = pBuf->idFileSystem;
529 /* What is about 3 ?*/
530 if (myfs_stats->type == 0) {
531 myfs_stats->typename = (char *) malloc(11);
532 strcpy(myfs_stats->typename, "Local Disk");
533 } else {
534 myfs_stats->typename = (char *) malloc(13);
535 strcpy(myfs_stats->typename, "Other Device");
538 free(pBuf);
539 free(pFsInfo);
542 #ifndef __EMX__
543 int
544 gettimeofday (struct timeval* tvp, void *p)
546 DATETIME pdt = {0};
547 if (p != NULL) /* what is "p"? */
548 return 0;
550 /* Since MC only calls this func from get_random_hint we return
551 * some value, not exactly the "correct" one
553 DosGetDateTime(&pdt);
554 tvp->tv_usec = (pdt.hours * 60 + pdt.minutes) * 60 + pdt.seconds;
555 /* Number of milliseconds since Windows started */
556 tvp->tv_sec = tvp->tv_usec * 1000 + pdt.hundredths * 10;
557 return 0;
559 #endif
561 /* FAKE functions */
563 int
564 look_for_exe(const char* pathname)
566 int j;
567 char *p;
568 int lgh = strlen(pathname);
570 if (lgh < 4) {
571 return 0;
572 } else {
573 p = (char *) pathname;
574 for (j=0; j<lgh-4; j++) {
575 p++;
577 if (!stricmp(p, ".exe") ||
578 !stricmp(p, ".bat") ||
579 !stricmp(p, ".com") ||
580 !stricmp(p, ".cmd")) {
581 return 1;
584 return 0;
587 int
588 lstat (const char* pathname, struct stat *buffer)
590 int rc = stat (pathname, buffer);
591 #ifdef __BORLANDC__
592 if (rc == 0) {
593 if (!(buffer->st_mode & S_IFDIR)) {
594 if (!look_for_exe(pathname)) {
595 buffer->st_mode &= !S_IXUSR & !S_IXGRP & !S_IXOTH;
599 #endif
600 return rc;
603 int
604 getuid ()
606 return 0;
609 int
610 getgid ()
612 return 0;
615 int
616 readlink (char* path, char* buf, int size)
618 return -1;
621 int
622 symlink (char *n1, char *n2)
624 return -1;
627 int
628 link (char *p1, char *p2)
630 return -1;
633 int
634 chown (char *path, int owner, int group)
636 return -1;
639 int
640 mknod (char *path, int mode, int dev)
642 return -1;
645 void
646 init_uid_gid_cache (void)
648 return;
651 int
652 mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
654 return -1;
657 int
658 mc_doublepclose (int pipe, pid_t pid)
660 return 0;
663 #ifndef HAVE_VFS
664 /*hacks to get it compile, remove these after vfs works */
665 char *
666 vfs_get_current_dir (void)
668 return NULL;
670 #endif
672 int
673 vfs_current_is_extfs (void)
675 return 0;
678 #ifndef HAVE_VFS
679 int
680 vfs_file_is_ftp (char *filename)
682 return 0;
686 mc_utime (char *path, void *times)
688 return 0;
692 void
693 extfs_run (char *file)
695 return;
697 #endif
699 void *
700 getgrent(void) { return NULL; }
702 void
703 setgrent(void) {}
705 void
706 endgrent(void) {}
709 setreuid(uid_t ruid, uid_t euid) { return -1; }
711 pid_t
712 setsid(void) { return (pid_t)-1; }
715 mkfifo(const char *path, mode_t mode) { return -1; }
717 int
718 socketpair(int i, int i1, int i2, int *i3)
720 return -1;
724 #ifndef HAVE_VFS
726 mc_chdir(char *pathname)
728 APIRET ret;
729 register int lgh = strlen(pathname);
731 /* Set the current drive */
732 if (lgh == 0) {
733 return -1;
734 } else {
735 /* First set the default drive */
736 if (lgh > 1) {
737 if (pathname[1] == ':') {
738 ret = DosSetDefaultDisk(toupper(pathname[0]) - 'A' + 1);
741 /* After that, set the current dir! */
742 ret = DosSetCurrentDir(pathname);
744 return ret;
748 mc_chmod(char *pathName, int unxmode)
750 /* OS/2 does not need S_REG */
751 int os2Mode = unxmode & 0x0FFF;
752 return chmod(pathName, os2Mode);
754 #endif
756 static int
757 conv_os2_unx_rc(int os2rc)
759 int errCode;
760 switch (os2rc) {
761 case ERROR_FILE_NOT_FOUND:
762 case ERROR_PATH_NOT_FOUND:
763 case ERROR_FILENAME_EXCED_RANGE:
764 errCode = ENOENT;
765 break;
766 case ERROR_NOT_DOS_DISK:
767 case ERROR_SHARING_VIOLATION:
768 case ERROR_SHARING_BUFFER_EXCEEDED:
769 case ERROR_ACCESS_DENIED:
770 errCode = EACCES;
771 break;
772 case ERROR_INVALID_PARAMETER:
773 errCode = EINVAL;
774 break;
775 default:
776 errCode = EINVAL;
777 break;
779 return errCode;
782 #ifndef HAVE_VFS
784 mc_open (char *file, int flags, int pmode)
786 return open(file, (flags | O_BINARY), pmode);
790 mc_unlink(char *pathName)
792 /* Use OS/2 API to delete a file, if the file is set as read-only,
793 the file will be deleted without asking the user! */
794 APIRET rc;
795 rc = DosDelete(pathName);
796 if (!rc) {
797 return 0;
799 if (rc == ERROR_ACCESS_DENIED) {
800 chmod(pathName, (S_IREAD|S_IWRITE));
801 rc = DosDelete(pathName);
802 if (rc) {
803 errno = conv_os2_unx_rc(rc) ;
804 return -1;
805 } else {
806 return 0;
808 } else {
809 errno = conv_os2_unx_rc(rc) ;
810 return -1;
813 #endif
815 char *
816 get_default_editor (void)
818 char *tmp;
819 APIRET rc;
820 char pathValue[5] = "PATH";
821 UCHAR searchResult[MC_MAXPATHLEN + 1];
823 /* EPM is not always be installed */
824 rc = DosSearchPath((SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY),
825 (PSZ) pathValue,
826 "EPM.EXE",
827 searchResult,
828 sizeof(searchResult));
829 if (rc != 0) {
830 /* The system editor is always there */
831 return strdup("e.exe");
832 } else {
833 /* Let it be searched from my_system */
834 return strdup("epm.exe");
838 /* get_default_shell
839 Get the default shell for the current hardware platform
840 TODO: Get the value of %OS2_SHELL% or %SHELL%: which one?
842 char *
843 get_default_shell()
845 return getenv ("COMSPEC");
849 errno_dir_not_empty (int err)
851 if (err == ENOTEMPTY)
852 return 1;
853 return 0;
856 /* The MC library directory is by default the directory where mc.exe
857 is situated. It is recommended to specify this directory via MCHOME
858 environment variable, otherwise you will be unable to rename mc.exe */
859 char *
860 get_mc_lib_dir ()
862 HMODULE mc_hm;
863 int rc;
864 char *cur = NULL;
865 char *mchome = getenv("MCHOME");
867 if (mchome && *mchome)
868 return mchome;
869 mchome = malloc(MC_MAXPATHLEN);
870 rc = DosQueryModuleHandle ("MC.EXE", &mc_hm);
871 if (!rc)
872 rc = DosQueryModuleName (mc_hm, MC_MAXPATHLEN, mchome);
873 if (!rc)
875 for (cur = mchome + strlen(mchome); \
876 (cur > mchome) && (*cur != PATH_SEP); cur--);
877 *cur = 0;
878 cur = strdup(mchome);
879 free(mchome);
881 if (!cur || !*cur) {
882 free(cur);
883 return "C:\\MC";
885 return cur;
888 int get_user_rights (struct stat *buf)
890 return 2;
892 void init_groups (void)
895 void delete_groups (void)