2000-06-05 H.J. Lu <hjl@gnu.org>
[binutils.git] / libiberty / mpw.c
blobca3ae412d25c1612f33138a47d7737a88ddcf39f
1 /* MPW-Unix compatibility library.
2 Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
4 This file is part of the libiberty library.
5 Libiberty is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 Libiberty is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with libiberty; see the file COPYING.LIB. If
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
20 /* This should only be compiled and linked under MPW. */
22 #include "mpw.h"
24 #include <stdlib.h>
26 #ifndef USE_MW_HEADERS
27 #include <sys/time.h>
28 #include <sys/resource.h>
29 #endif
31 #include <Types.h>
32 #include <Files.h>
34 #include <Timer.h>
36 /* Initialize to 0 at first, then set to errno_max() later. */
38 int sys_nerr = 0;
40 /* Debug flag for pathname hacking. Set this to one and rebuild. */
42 int DebugPI = -1;
44 void
45 mpwify_filename(char *unixname, char *macname)
47 int i, j;
49 /* (should truncate 255 chars from end of name, not beginning) */
50 if (strlen (unixname) > 255)
52 fprintf (stderr, "Pathname \"%s\" is too long for Macs, truncating\n",
53 unixname);
55 j = 0;
56 /* If you're going to end up with one or more colons in the middle of a
57 path after an all-Unix relative path is translated, you must add a
58 colon on the front, so that the first component is not thought to be
59 a disk name. */
60 if (unixname[0] != '/' && ! strchr (unixname, ':') && strchr (unixname, '/'))
62 macname[j++] = ':';
64 for (i = 0; unixname[i] != '\0' && i < 255; ++i)
66 if (i == 0 && unixname[i] == '/')
68 if (strncmp (unixname, "/tmp/", 5) == 0)
70 /* A temporary name, make a more Mac-flavored tmpname. */
71 /* A better choice would be {Boot}Trash:foo, but
72 that would require being able to identify the
73 boot disk's and trashcan's name. Another option
74 would be to have an env var, so user can point it
75 at a ramdisk. */
76 macname[j++] = ':';
77 macname[j++] = 't';
78 macname[j++] = 'm';
79 macname[j++] = 'p';
80 macname[j++] = '_';
81 i += 4;
83 else
85 /* Don't copy the leading slash. */
88 else if (unixname[i] == ':' && unixname[i+1] == '/')
90 macname[j++] = ':';
91 i += 1;
93 else if (unixname[i] == '.' && unixname[i+1] == '/')
95 macname[j++] = ':';
96 i += 1;
98 else if (unixname[i] == '.' && unixname[i+1] == '.' && unixname[i+2] == '/')
100 macname[j++] = ':';
101 macname[j++] = ':';
102 i += 2;
104 else if (unixname[i] == '/')
106 macname[j++] = ':';
108 else
110 macname[j++] = unixname[i];
113 macname[j] = '\0';
114 /* Allow for getting the debug flag from an env var; quite useful. */
115 if (DebugPI < 0)
116 DebugPI = (*(getenv ("DEBUG_PATHNAMES")) == '1' ? 1 : 0);
117 if (DebugPI)
119 fprintf (stderr, "# Made \"%s\"\n", unixname);
120 fprintf (stderr, "# into \"%s\"\n", macname);
124 /* MPW-flavored basename finder. */
126 char *
127 mpw_basename (name)
128 char *name;
130 char *base = name;
132 while (*name)
134 if (*name++ == ':')
136 base = name;
139 return base;
142 /* Mixed MPW/Unix basename finder. This can be led astray by
143 filenames with slashes in them and come up with a basename that
144 either corresponds to no file or (worse) to some other file, so
145 should only be tried if other methods of finding a file via a
146 basename have failed. */
148 char *
149 mpw_mixed_basename (name)
150 char *name;
152 char *base = name;
154 while (*name)
156 if (*name == '/' || *name == ':')
158 base = name + 1;
160 ++name;
162 return base;
165 /* This function is fopen() modified to create files that are type TEXT
166 or 'BIN ', and always of type 'MPS '. */
168 FILE *
169 mpw_fopen (char *name, char *mode)
171 #undef fopen
172 int errnum;
173 FILE *fp;
174 char tmpname[256];
176 mpwify_filename (name, tmpname);
177 PROGRESS (1);
178 fp = fopen (tmpname, mode);
179 errnum = errno;
181 /* If writing, need to set type and creator usefully. */
182 if (strchr (mode, 'w'))
184 char *pname = (char *) malloc (strlen (tmpname) + 2);
185 OSErr e;
186 struct FInfo fi;
188 pname[0] = strlen (tmpname);
189 strcpy (pname+1, tmpname);
191 e = GetFInfo ((ConstStr255Param) pname, 0, &fi);
192 /* should do spiffier error handling */
193 if (e != 0)
194 fprintf(stderr, "GetFInfo returns %d\n", e);
195 if (strchr (mode, 'b'))
197 fi.fdType = (OSType) 'BIN ';
199 else
201 fi.fdType = (OSType) 'TEXT';
203 fi.fdCreator = (OSType) 'MPS ';
204 e = SetFInfo ((ConstStr255Param) pname, 0, &fi);
205 if (e != 0)
206 fprintf(stderr, "SetFInfo returns %d\n", e);
207 free (pname);
209 if (fp == NULL)
210 errno = errnum;
211 return fp;
214 /* This is a version of fseek() modified to fill the file with zeros
215 if seeking past the end of it. */
217 #define ZEROBLKSIZE 4096
219 char zeros[ZEROBLKSIZE];
222 mpw_fseek (FILE *fp, int offset, int whence)
224 #undef fseek
225 int cursize, numleft;
227 PROGRESS (1);
228 if (whence == SEEK_SET)
230 fseek (fp, 0, SEEK_END);
231 cursize = ftell (fp);
232 if (offset > cursize)
234 numleft = offset - cursize;
235 while (numleft > ZEROBLKSIZE)
237 /* This might fail, should check for that. */
238 PROGRESS (1);
239 fwrite (zeros, 1, ZEROBLKSIZE, fp);
240 numleft -= ZEROBLKSIZE;
242 PROGRESS (1);
243 fwrite (zeros, 1, numleft, fp);
244 fflush (fp);
247 return fseek (fp, offset, whence);
251 mpw_fread (char *ptr, int size, int nitems, FILE *stream)
253 #undef fread
254 int rslt;
256 PROGRESS (1);
257 rslt = fread (ptr, size, nitems, stream);
258 PROGRESS (1);
259 return rslt;
263 mpw_fwrite (char *ptr, int size, int nitems, FILE *stream)
265 #undef fwrite
266 int rslt;
268 PROGRESS (1);
269 rslt = fwrite (ptr, size, nitems, stream);
270 PROGRESS (1);
271 return rslt;
275 link ()
277 fprintf (stderr, "link not available!\n");
278 mpw_abort ();
282 fork ()
284 fprintf (stderr, "fork not available!\n");
285 mpw_abort ();
289 vfork ()
291 fprintf (stderr, "vfork not available!\n");
292 mpw_abort ();
293 return (-1);
297 pipe (int *fd)
299 fprintf (stderr, "pipe not available!\n");
300 mpw_abort ();
301 return (-1);
304 #ifndef USE_MW_HEADERS
306 execvp (char *file, char **argv)
308 fprintf (stderr, "execvp not available!\n");
309 mpw_abort ();
310 return (-1);
314 execv (char *path, char **argv)
316 fprintf (stderr, "execv not available!\n");
317 mpw_abort ();
318 return (-1);
320 #endif
323 kill (int pid, int sig)
325 fprintf (stderr, "kill not available!\n");
326 mpw_abort ();
327 return (-1);
331 wait (int *status)
333 *status = 0;
334 return 0;
337 #ifndef USE_MW_HEADERS
339 sleep (int seconds)
341 unsigned long start_time, now;
343 time (&start_time);
345 while (1)
347 PROGRESS (1);
348 time (&now);
349 if (now > start_time + seconds)
350 return 0;
353 #endif
355 void
356 putenv (char *str)
358 /* The GCC driver calls this to do things for collect2, but we
359 don't care about collect2. */
363 chmod (char *path, int mode)
365 /* Pretend it was all OK. */
366 return 0;
369 #ifndef USE_MW_HEADERS
371 getuid ()
373 /* One value is as good as another... */
374 return 0;
378 getgid ()
380 /* One value is as good as another... */
381 return 0;
383 #endif
385 /* Instead of coredumping, which is not a normal Mac facility, we
386 drop into Macsbug. If we then "g" from Macsbug, the program will
387 exit cleanly. */
389 void
390 mpw_abort ()
392 /* Make sure no output still buffered up, then zap into MacsBug. */
393 fflush(stdout);
394 fflush(stderr);
395 printf("## Abort! ##\n");
396 #ifdef MPW_SADE
397 SysError(8005);
398 #else
399 Debugger();
400 #endif
401 /* "g" in MacsBug will then cause a regular error exit. */
402 exit (1);
405 /* Imitation getrusage based on the ANSI clock() function. */
408 getrusage (int who, struct rusage *rusage)
410 int clk = clock ();
412 #if 0
413 rusage->ru_utime.tv_sec = clk / CLOCKS_PER_SEC;
414 rusage->ru_utime.tv_usec = ((clk * 1000) / CLOCKS_PER_SEC) * 1000;
415 rusage->ru_stime.tv_sec = 0;
416 rusage->ru_stime.tv_usec = 0;
417 #endif
421 sbrk ()
423 return 0;
426 #ifndef USE_MW_HEADERS
428 isatty (int fd)
430 return 0;
433 /* This is inherited from Timothy Murray's Posix library. */
435 #include "utime.h"
438 utime (char *filename, struct utimbuf *times)
440 CInfoPBRec cipbr;
441 HFileInfo *fpb = (HFileInfo *) &cipbr;
442 DirInfo *dpb = (DirInfo *) &cipbr;
443 unsigned char pname[256];
444 short err;
446 strcpy ((char *) pname, filename);
447 c2pstr (pname);
449 dpb->ioDrDirID = 0L;
450 fpb->ioNamePtr = pname;
451 fpb->ioVRefNum = 0;
452 fpb->ioFDirIndex = 0;
453 fpb->ioFVersNum = 0;
454 err = PBGetCatInfo (&cipbr, 0);
455 if (err != noErr) {
456 errno = ENOENT;
457 return -1;
459 dpb->ioDrDirID = 0L;
460 fpb->ioFlMdDat = times->modtime;
461 fpb->ioFlCrDat = times->actime;
462 err = PBSetCatInfo (&cipbr, 0);
463 if (err != noErr) {
464 errno = EACCES;
465 return -1;
467 return 0;
471 mkdir (char *path, int mode)
473 errno = ENOSYS;
474 return -1;
478 rmdir ()
480 errno = ENOSYS;
481 return -1;
483 #endif
485 chown ()
487 errno = ENOSYS;
488 return -1;
491 char *myenviron[] = {NULL};
493 char **environ = myenviron;
495 #ifndef USE_MW_HEADERS
497 /* Minimal 'stat' emulation: tells directories from files and
498 gives length and mtime.
500 Derived from code written by Guido van Rossum, CWI, Amsterdam
501 and placed by him in the public domain. */
503 extern int __uid, __gid;
505 int __uid = 0;
506 int __gid = 0;
508 /* Bits in ioFlAttrib: */
509 #define LOCKBIT (1<<0) /* File locked */
510 #define DIRBIT (1<<4) /* It's a directory */
512 /* Macified "stat" in which filename is given relative to a directory,
513 specified by long DirID. */
515 static int
516 _stat (char *name, long dirid, struct stat *buf)
518 CInfoPBRec cipbr;
519 HFileInfo *fpb = (HFileInfo*) &cipbr;
520 DirInfo *dpb = (DirInfo*) &cipbr;
521 Str255 pname;
522 short err;
524 /* Make a temp copy of the name and pascalize. */
525 strcpy ((char *) pname, name);
526 c2pstr (pname);
528 cipbr.dirInfo.ioDrDirID = dirid;
529 cipbr.hFileInfo.ioNamePtr = pname;
530 cipbr.hFileInfo.ioVRefNum = 0;
531 cipbr.hFileInfo.ioFDirIndex = 0;
532 cipbr.hFileInfo.ioFVersNum = 0;
533 err = PBGetCatInfo (&cipbr, 0);
534 if (err != noErr)
536 errno = ENOENT;
537 return -1;
539 /* Mac files are readable if they can be accessed at all. */
540 buf->st_mode = 0444;
541 /* Mark unlocked files as writeable. */
542 if (!(fpb->ioFlAttrib & LOCKBIT))
543 buf->st_mode |= 0222;
544 if (fpb->ioFlAttrib & DIRBIT)
546 /* Mark directories as "executable". */
547 buf->st_mode |= 0111 | S_IFDIR;
548 buf->st_size = dpb->ioDrNmFls;
549 buf->st_rsize = 0;
551 else
553 buf->st_mode |= S_IFREG;
554 /* Mark apps as "executable". */
555 if (fpb->ioFlFndrInfo.fdType == 'APPL')
556 buf->st_mode |= 0111;
557 /* Fill in the sizes of data and resource forks. */
558 buf->st_size = fpb->ioFlLgLen;
559 buf->st_rsize = fpb->ioFlRLgLen;
561 /* Fill in various times. */
562 buf->st_atime = fpb->ioFlCrDat;
563 buf->st_mtime = fpb->ioFlMdDat;
564 buf->st_ctime = fpb->ioFlCrDat;
565 /* Set up an imitation inode number. */
566 buf->st_ino = (unsigned short) fpb->ioDirID;
567 /* Set up an imitation device. */
568 GetVRefNum (buf->st_ino, &buf->st_dev);
569 buf->st_uid = __uid;
570 buf->st_gid = __gid;
571 /* buf->st_FlFndrInfo = fpb->ioFlFndrInfo; */
572 return 0;
575 /* stat() sets up an empty dirid. */
578 stat (char *path, struct stat *buf)
580 long rslt, errnum;
581 char tmpname[256];
583 mpwify_filename (path, tmpname);
584 if (DebugPI)
585 fprintf (stderr, "# stat (%s, %x)", tmpname, buf);
586 PROGRESS (1);
587 rslt = _stat (tmpname, 0L, buf);
588 errnum = errno;
589 if (DebugPI)
591 fprintf (stderr, " -> %d", rslt);
592 if (rslt != 0)
593 fprintf (stderr, " (errno is %d)", errnum);
594 fprintf (stderr, "\n");
595 fflush (stderr);
597 if (rslt != 0)
598 errno = errnum;
599 return rslt;
603 fstat (int fd, struct stat *buf)
605 FCBPBRec fcb;
606 FILE *fp;
607 Str255 pathname;
608 long dirid = 0L, temp;
609 long rslt, errnum;
610 short err;
612 if (DebugPI < 0)
613 DebugPI = (*(getenv ("DEBUG_PATHNAMES")) == '1' ? 1 : 0);
614 if (DebugPI)
615 fprintf (stderr, "# fstat (%d, %x)", fd, buf);
616 PROGRESS (1);
617 pathname[0] = 0;
618 #ifdef FIOFNAME
619 /* Use an MPW-specific ioctl to get the pathname associated with
620 the file descriptor. */
621 ioctl (fd, FIOFNAME, (long *) pathname);
622 #else
623 you lose
624 #endif
625 if (DebugPI)
626 fprintf (stderr, " (name is %s)", pathname);
627 dirid = 0L /* fcb.ioFCBParID */ ;
628 rslt = _stat ((char *) pathname, dirid, buf);
629 errnum = errno;
630 if (DebugPI)
632 fprintf (stderr, " -> %d", rslt);
633 if (rslt != 0)
634 fprintf (stderr, " (errno is %d)", errnum);
635 fprintf (stderr, "\n");
636 fflush (stderr);
638 if (rslt != 0)
639 errno = errnum;
640 return rslt;
643 #endif /* n USE_MW_HEADERS */
645 chdir ()
647 errno = ENOSYS;
648 return (-1);
651 char *
652 getcwd (char *buf, int size)
654 if (buf == NULL)
655 buf = (char *) malloc (size);
656 strcpy(buf, ":");
657 return buf;
660 /* This should probably be more elaborate for MPW. */
662 char *
663 getpwd ()
665 return ":";
669 mpw_open (char *filename, int arg2, int arg3)
671 #undef open
672 int fd, errnum = 0;
673 char tmpname[256];
675 mpwify_filename (filename, tmpname);
676 fd = open (tmpname, arg2);
677 errnum = errno;
679 if (DebugPI)
681 fprintf (stderr, "# open (%s, %d, %d)", tmpname, arg2, arg3);
682 fprintf (stderr, " -> %d", fd);
683 if (fd == -1)
684 fprintf (stderr, " (errno is %d)", errnum);
685 fprintf (stderr, "\n");
687 if (fd == -1)
688 errno = errnum;
689 return fd;
693 mpw_access (char *filename, unsigned int cmd)
695 #undef access
697 int rslt, errnum = 0;
698 struct stat st;
699 char tmpname[256];
701 mpwify_filename (filename, tmpname);
702 if (cmd & R_OK || cmd & X_OK)
704 rslt = stat (tmpname, &st);
705 errnum = errno;
706 if (rslt >= 0)
708 if ((((st.st_mode & 004) == 0) && (cmd & R_OK))
709 || (((st.st_mode & 002) == 0) && (cmd & W_OK))
710 || (((st.st_mode & 001) == 0) && (cmd & X_OK)))
712 rslt = -1;
713 errnum = EACCES;
717 if (DebugPI)
719 fprintf (stderr, "# mpw_access (%s, %d)", tmpname, cmd);
720 fprintf (stderr, " -> %d", rslt);
721 if (rslt != 0)
722 fprintf (stderr, " (errno is %d)", errnum);
723 fprintf (stderr, "\n");
725 if (rslt != 0)
726 errno = errnum;
727 return rslt;
730 /* The MPW library creat() has no mode argument. */
733 mpw_creat (char *path, /* mode_t */ int mode)
735 #undef creat
737 #ifdef USE_MW_HEADERS
738 return creat (path, mode);
739 #else
740 return creat (path);
741 #endif
744 /* This is a hack to get control in an MPW tool before it crashes the
745 machine. */
747 mpw_special_init (name)
748 char *name;
750 if (strstr (name, "DEBUG"))
751 DebugStr("\pat beginning of program");
754 static int current_umask;
757 umask(int mask)
759 int oldmask = current_umask;
761 current_umask = mask;
762 return oldmask;
765 /* Cursor-spinning stuff that includes metering of spin rate and delays. */
767 /* Nonzero when cursor spinning has been set up properly. */
769 int cursor_inited;
771 /* Nonzero if spin should be measured and excessive delays reported. */
773 int measure_spin;
775 /* Nonzero if spin histogram and rate data should be written out. */
777 int dump_spin_data;
779 long warning_threshold = 400000;
781 long bucket_size = 1024;
783 long bucket_power = 10;
785 long numbuckets = 300;
787 int *delay_counts;
789 int overflow_count;
791 char *current_progress;
793 static UnsignedWide last_microseconds;
795 static char *last_spin_file = "";
797 static int last_spin_line;
799 void
800 warn_if_spin_delay (char *file, int line)
802 long diff, ix;
803 UnsignedWide now;
805 Microseconds(&now);
807 diff = now.lo - last_microseconds.lo;
809 if (diff > warning_threshold)
810 fprintf (stderr, "# %s: %ld.%06ld sec delay getting from %s:%d to %s:%d\n",
811 (current_progress ? current_progress : ""),
812 diff / 1000000, diff % 1000000,
813 last_spin_file, last_spin_line, file, line);
814 if (dump_spin_data)
816 if (diff >= 0)
818 ix = diff >> bucket_power;
819 if (ix >= 0 && ix < numbuckets && delay_counts != NULL)
820 ++delay_counts[ix];
821 else
822 ++overflow_count;
824 else
825 fprintf (stderr, "raw diff is %ld (?)\n", diff);
829 void
830 record_for_spin_delay (char *file, int line)
832 Microseconds (&last_microseconds);
833 last_spin_file = file;
834 last_spin_line = line;
837 void
838 mpw_start_progress (char *str, int n, char *file, int line)
840 int i;
841 char *measure, *threshold;
843 if (!cursor_inited)
845 InitCursorCtl (nil);
846 cursor_inited = 1;
847 record_for_spin_delay (file, line);
848 measure = getenv ("MEASURE_SPIN");
849 if (measure != NULL && measure[0] != '\0')
851 measure_spin = 1;
852 if (strcmp (measure, "all") == 0)
853 dump_spin_data = 1;
855 threshold = getenv ("SPIN_WARN_THRESHOLD");
856 if (threshold != NULL && threshold[0] != '\0')
857 warning_threshold = atol (threshold);
858 if (dump_spin_data)
860 if (delay_counts == NULL)
861 delay_counts = (int *) malloc (numbuckets * sizeof (int));
862 for (i = 0; i < numbuckets; ++i)
863 delay_counts[i] = 0;
864 overflow_count = 0;
867 current_progress = str;
869 sys_nerr = errno_max ();
871 mpw_special_init (str);
874 void
875 mpw_progress (int n)
877 SpinCursor (32);
880 void
881 mpw_progress_measured (int n, char *file, int line)
883 if (measure_spin)
884 warn_if_spin_delay (file, line);
885 SpinCursor (32);
886 if (measure_spin)
887 record_for_spin_delay (file, line);
890 void
891 mpw_end_progress (char *str, char *file, int line)
893 long i, delay, count = 0, sum = 0, avgdelay, spinrate;
894 long curpower = 0, curgroup = 0;
896 /* Warn if it's been a while since the last spin. */
897 if (measure_spin)
898 warn_if_spin_delay (file, line);
900 /* Dump all the nonzero delay counts and an approximation of the delay. */
901 if (dump_spin_data && delay_counts != NULL)
903 for (i = 0; i < numbuckets; ++i)
905 delay = (i + 1) * bucket_size;
906 sum += delay_counts[i] * (i + 1);
907 count += delay_counts[i];
908 if (delay <= (1 << curpower))
910 curgroup += delay_counts[i];
912 else
914 if (curgroup > 0)
915 fprintf (stderr,
916 "# %s: %d delays between %ld.%06ld and %ld.%06ld sec\n",
917 (str ? str : ""),
918 curgroup,
919 (1 << curpower) / 1000000,
920 (1 << curpower) % 1000000,
921 (1 << (curpower + 1)) / 1000000,
922 (1 << (curpower + 1)) % 1000000);
923 ++curpower;
924 curgroup = 0;
927 if (count > 0)
929 avgdelay = (sum * bucket_size) / count;
930 spinrate = 1000000 / avgdelay;
931 fprintf (stderr, "# %s: Average spin rate is %d times/sec\n",
932 (str ? str : ""), spinrate);
937 #ifdef PROGRESS_TEST
939 /* Test program. */
941 main ()
943 int i, j;
944 double x = 1.0, y = 2.4;
945 long start = Microseconds (), tm; FIXME
947 START_PROGRESS ("hi", 0);
949 for (i = 0; i < 1000; ++i)
951 PROGRESS (1);
953 for (j = 0; j < (i * 100); ++j)
955 x += (x * y) / j;
959 END_PROGRESS ("hi");
961 tm = Microseconds () - start;
963 printf ("Total time is %d.%d secs\n", tm / 1000000, tm % 1000000);
966 #endif
968 #ifdef USE_MW_HEADERS
969 /* Empty definitions for Metrowerks' SIOUX console library. */
971 #ifndef __CONSOLE__
972 #include <console.h>
973 #endif
975 short
976 InstallConsole(short fd)
978 #pragma unused (fd)
979 return 0;
982 void
983 RemoveConsole(void)
987 long
988 WriteCharsToConsole(char *buf, long n)
990 #pragma unused (buf, n)
991 return 0;
994 long ReadCharsFromConsole(char *buf, long n)
996 #pragma unused (buf, n)
997 return 0;
1000 extern char *
1001 __ttyname(long fd)
1003 static char *__devicename = "null device";
1005 if (fd >= 0 && fd <= 2)
1006 return (__devicename);
1007 return NULL;
1010 #endif