1/* MPW-Unix compatibility library.
2   Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
3
4This file is part of the libiberty library.
5Libiberty is free software; you can redistribute it and/or
6modify it under the terms of the GNU Library General Public
7License as published by the Free Software Foundation; either
8version 2 of the License, or (at your option) any later version.
9
10Libiberty is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13Library General Public License for more details.
14
15You should have received a copy of the GNU Library General Public
16License along with libiberty; see the file COPYING.LIB.  If
17not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18Boston, MA 02111-1307, USA.  */
19
20/* This should only be compiled and linked under MPW. */
21
22#include "mpw.h"
23
24#include <stdlib.h>
25
26#ifndef USE_MW_HEADERS
27#include <sys/time.h>
28#include <sys/resource.h>
29#endif
30
31#include <Types.h>
32#include <Files.h>
33
34#include <Timer.h>
35
36/* Initialize to 0 at first, then set to errno_max() later.  */
37
38int sys_nerr = 0;
39
40/* Debug flag for pathname hacking.  Set this to one and rebuild. */
41
42int DebugPI = -1;
43
44void
45mpwify_filename(char *unixname, char *macname)
46{
47  int i, j;
48
49  /* (should truncate 255 chars from end of name, not beginning) */
50  if (strlen (unixname) > 255)
51    {
52      fprintf (stderr, "Pathname \"%s\" is too long for Macs, truncating\n",
53	       unixname);
54    }
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, '/'))
61    {
62      macname[j++] = ':';
63    }
64  for (i = 0; unixname[i] != '\0' && i < 255; ++i)
65    {
66      if (i == 0 && unixname[i] == '/')
67	{
68	  if (strncmp (unixname, "/tmp/", 5) == 0)
69	    {
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;
82	    }
83	  else
84	    {
85	      /* Don't copy the leading slash. */
86	    }
87	}
88      else if (unixname[i] == ':' && unixname[i+1] == '/')
89	{
90	  macname[j++] = ':';
91	  i += 1;
92	}
93      else if (unixname[i] == '.' && unixname[i+1] == '/')
94	{
95	  macname[j++] = ':';
96	  i += 1;
97	}
98      else if (unixname[i] == '.' && unixname[i+1] == '.' && unixname[i+2] == '/')
99	{
100	  macname[j++] = ':';
101	  macname[j++] = ':';
102	  i += 2;
103	}
104      else if (unixname[i] == '/')
105	{
106	  macname[j++] = ':';
107	}
108      else
109	{
110	  macname[j++] = unixname[i];
111	}
112    }
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)
118    {
119      fprintf (stderr, "# Made \"%s\"\n", unixname);
120      fprintf (stderr, "# into \"%s\"\n", macname);
121    }
122}
123
124/* MPW-flavored basename finder. */
125
126char *
127mpw_basename (name)
128  char *name;
129{
130  char *base = name;
131
132  while (*name)
133    {
134      if (*name++ == ':')
135	{
136	  base = name;
137	}
138    }
139  return base;
140}
141
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.  */
147
148char *
149mpw_mixed_basename (name)
150  char *name;
151{
152  char *base = name;
153
154  while (*name)
155    {
156      if (*name == '/' || *name == ':')
157	{
158	  base = name + 1;
159	}
160      ++name;
161    }
162  return base;
163}
164
165/* This function is fopen() modified to create files that are type TEXT
166   or 'BIN ', and always of type 'MPS '.  */
167
168FILE *
169mpw_fopen (char *name, char *mode)
170{
171#undef fopen
172  int errnum;
173  FILE *fp;
174  char tmpname[256];
175
176  mpwify_filename (name, tmpname);
177  PROGRESS (1);
178  fp = fopen (tmpname, mode);
179  errnum = errno;
180
181  /* If writing, need to set type and creator usefully. */
182  if (strchr (mode, 'w'))
183    {
184      char *pname = (char *) malloc (strlen (tmpname) + 2);
185      OSErr e;
186      struct FInfo fi;
187
188      pname[0] = strlen (tmpname);
189      strcpy (pname+1, tmpname);
190
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'))
196	{
197	  fi.fdType = (OSType) 'BIN ';
198	}
199      else
200	{
201	  fi.fdType = (OSType) 'TEXT';
202	}
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);
208    }
209  if (fp == NULL)
210    errno = errnum;
211  return fp;
212}
213
214/* This is a version of fseek() modified to fill the file with zeros
215   if seeking past the end of it.  */
216
217#define ZEROBLKSIZE 4096
218
219char zeros[ZEROBLKSIZE];
220
221int
222mpw_fseek (FILE *fp, int offset, int whence)
223{
224#undef fseek
225  int cursize, numleft;
226
227  PROGRESS (1);
228  if (whence == SEEK_SET)
229    {
230      fseek (fp, 0, SEEK_END);
231      cursize = ftell (fp);
232      if (offset > cursize)
233	{
234	  numleft = offset - cursize;
235	  while (numleft > ZEROBLKSIZE)
236	    {
237	      /* This might fail, should check for that. */
238	      PROGRESS (1);
239	      fwrite (zeros, 1, ZEROBLKSIZE, fp);
240	      numleft -= ZEROBLKSIZE;
241	    }
242	  PROGRESS (1);
243	  fwrite (zeros, 1, numleft, fp);
244	  fflush (fp);
245	}
246    }
247  return fseek (fp, offset, whence);
248}
249
250int
251mpw_fread (char *ptr, int size, int nitems, FILE *stream)
252{
253#undef fread
254  int rslt;
255
256  PROGRESS (1);
257  rslt = fread (ptr, size, nitems, stream);
258  PROGRESS (1);
259  return rslt;
260}
261
262int
263mpw_fwrite (char *ptr, int size, int nitems, FILE *stream)
264{
265#undef fwrite
266  int rslt;
267
268  PROGRESS (1);
269  rslt = fwrite (ptr, size, nitems, stream);
270  PROGRESS (1);
271  return rslt;
272}
273
274int
275link ()
276{
277  fprintf (stderr, "link not available!\n");
278  mpw_abort ();
279}
280
281int
282fork ()
283{
284  fprintf (stderr, "fork not available!\n");
285  mpw_abort ();
286}
287
288int
289vfork ()
290{
291  fprintf (stderr, "vfork not available!\n");
292  mpw_abort ();
293  return (-1);
294}
295
296int
297pipe (int *fd)
298{
299  fprintf (stderr, "pipe not available!\n");
300  mpw_abort ();
301  return (-1);
302}
303
304#ifndef USE_MW_HEADERS
305int
306execvp (char *file, char **argv)
307{
308  fprintf (stderr, "execvp not available!\n");
309  mpw_abort ();
310  return (-1);
311}
312
313int
314execv (char *path, char **argv)
315{
316  fprintf (stderr, "execv not available!\n");
317  mpw_abort ();
318  return (-1);
319}
320#endif
321
322int
323kill (int pid, int sig)
324{
325  fprintf (stderr, "kill not available!\n");
326  mpw_abort ();
327  return (-1);
328}
329
330int
331wait (int *status)
332{
333  *status = 0;
334  return 0;
335}
336
337#ifndef USE_MW_HEADERS
338int
339sleep (int seconds)
340{
341  unsigned long start_time, now;
342
343  time (&start_time);
344
345  while (1)
346    {
347      PROGRESS (1);
348      time (&now);
349      if (now > start_time + seconds)
350	return 0;
351    }
352}
353#endif
354
355void
356putenv (char *str)
357{
358  /* The GCC driver calls this to do things for collect2, but we
359     don't care about collect2. */
360}
361
362int
363chmod (char *path, int mode)
364{
365  /* Pretend it was all OK. */
366  return 0;
367}
368
369#ifndef USE_MW_HEADERS
370int
371getuid ()
372{
373  /* One value is as good as another... */
374  return 0;
375}
376
377int
378getgid ()
379{
380  /* One value is as good as another... */
381  return 0;
382}
383#endif
384
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. */
388
389void
390mpw_abort ()
391{
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);
403}
404
405/* Imitation getrusage based on the ANSI clock() function. */
406
407int
408getrusage (int who, struct rusage *rusage)
409{
410  int clk = clock ();
411
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
418}
419
420int
421sbrk ()
422{
423  return 0;
424}
425
426#ifndef USE_MW_HEADERS
427int
428isatty (int fd)
429{
430  return 0;
431}
432
433/* This is inherited from Timothy Murray's Posix library. */
434
435#include "utime.h"
436
437int
438utime (char *filename, struct utimbuf *times)
439{
440  CInfoPBRec cipbr;
441  HFileInfo *fpb = (HFileInfo *) &cipbr;
442  DirInfo *dpb = (DirInfo *) &cipbr;
443  unsigned char pname[256];
444  short err;
445
446  strcpy ((char *) pname, filename);
447  c2pstr (pname);
448
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;
458  }
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;
466  }
467  return 0;
468}
469
470int
471mkdir (char *path, int mode)
472{
473  errno = ENOSYS;
474  return -1;
475}
476
477int
478rmdir ()
479{
480  errno = ENOSYS;
481  return -1;
482}
483#endif
484
485chown ()
486{
487  errno = ENOSYS;
488  return -1;
489}
490
491char *myenviron[] = {NULL};
492
493char **environ = myenviron;
494
495#ifndef USE_MW_HEADERS
496
497/* Minimal 'stat' emulation: tells directories from files and
498   gives length and mtime.
499
500   Derived from code written by Guido van Rossum, CWI, Amsterdam
501   and placed by him in the public domain.  */
502
503extern int __uid, __gid;
504
505int __uid = 0;
506int __gid = 0;
507
508/* Bits in ioFlAttrib: */
509#define LOCKBIT	(1<<0)		/* File locked */
510#define DIRBIT	(1<<4)		/* It's a directory */
511
512/* Macified "stat" in which filename is given relative to a directory,
513   specified by long DirID.  */
514
515static int
516_stat (char *name, long dirid, struct stat *buf)
517{
518  CInfoPBRec cipbr;
519  HFileInfo *fpb = (HFileInfo*) &cipbr;
520  DirInfo *dpb = (DirInfo*) &cipbr;
521  Str255 pname;
522  short err;
523
524  /* Make a temp copy of the name and pascalize. */
525  strcpy ((char *) pname, name);
526  c2pstr (pname);
527
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)
535    {
536      errno = ENOENT;
537      return -1;
538    }
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)
545    {
546      /* Mark directories as "executable". */
547      buf->st_mode |= 0111 | S_IFDIR;
548      buf->st_size = dpb->ioDrNmFls;
549      buf->st_rsize = 0;
550    }
551  else
552    {
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;
560    }
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;
573}
574
575/* stat() sets up an empty dirid. */
576
577int
578stat (char *path, struct stat *buf)
579{
580  long rslt, errnum;
581  char tmpname[256];
582
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)
590    {
591      fprintf (stderr, " -> %d", rslt);
592      if (rslt != 0)
593	fprintf (stderr, " (errno is %d)", errnum);
594      fprintf (stderr, "\n");
595      fflush (stderr);
596    }
597  if (rslt != 0)
598    errno = errnum;
599  return rslt;
600}
601
602int
603fstat (int fd, struct stat *buf)
604{
605  FCBPBRec fcb;
606  FILE *fp;
607  Str255 pathname;
608  long dirid = 0L, temp;
609  long rslt, errnum;
610  short err;
611
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)
631    {
632      fprintf (stderr, " -> %d", rslt);
633      if (rslt != 0)
634	fprintf (stderr, " (errno is %d)", errnum);
635      fprintf (stderr, "\n");
636      fflush (stderr);
637    }
638  if (rslt != 0)
639    errno = errnum;
640  return rslt;
641}
642
643#endif /* n USE_MW_HEADERS */
644
645chdir ()
646{
647  errno = ENOSYS;
648  return (-1);
649}
650
651char *
652getcwd (char *buf, int size)
653{
654  if (buf == NULL)
655    buf = (char *) malloc (size);
656  strcpy(buf, ":");
657  return buf;
658}
659
660/* This should probably be more elaborate for MPW. */
661
662char *
663getpwd ()
664{
665  return ":";
666}
667
668int
669mpw_open (char *filename, int arg2, int arg3)
670{
671#undef open
672  int fd, errnum = 0;
673  char tmpname[256];
674
675  mpwify_filename (filename, tmpname);
676  fd = open (tmpname, arg2);
677  errnum = errno;
678
679  if (DebugPI)
680    {
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");
686    }
687  if (fd == -1)
688    errno = errnum;
689  return fd;
690}
691
692int
693mpw_access (char *filename, unsigned int cmd)
694{
695#undef access
696
697  int rslt, errnum = 0;
698  struct stat st;
699  char tmpname[256];
700
701  mpwify_filename (filename, tmpname);
702  if (cmd & R_OK || cmd & X_OK)
703    {
704      rslt = stat (tmpname, &st);
705      errnum = errno;
706      if (rslt >= 0)
707	{
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)))
711	    {
712	      rslt = -1;
713	      errnum = EACCES;
714	    }
715	}
716    }
717  if (DebugPI)
718    {
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");
724    }
725  if (rslt != 0)
726    errno = errnum;
727  return rslt;
728}
729
730/* The MPW library creat() has no mode argument. */
731
732int
733mpw_creat (char *path, /* mode_t */ int mode)
734{
735#undef creat
736
737#ifdef USE_MW_HEADERS
738  return creat (path, mode);
739#else
740  return creat (path);
741#endif
742}
743
744/* This is a hack to get control in an MPW tool before it crashes the
745   machine.  */
746
747mpw_special_init (name)
748     char *name;
749{
750  if (strstr (name, "DEBUG"))
751    DebugStr("\pat beginning of program");
752}
753
754static int current_umask;
755
756int
757umask(int mask)
758{
759  int oldmask = current_umask;
760
761  current_umask = mask;
762  return oldmask;
763}
764
765/* Cursor-spinning stuff that includes metering of spin rate and delays.  */
766
767/* Nonzero when cursor spinning has been set up properly.  */
768
769int cursor_inited;
770
771/* Nonzero if spin should be measured and excessive delays reported.  */
772
773int measure_spin;
774
775/* Nonzero if spin histogram and rate data should be written out.  */
776
777int dump_spin_data;
778
779long warning_threshold = 400000;
780
781long bucket_size = 1024;
782
783long bucket_power = 10;
784
785long numbuckets = 300;
786
787int *delay_counts;
788
789int overflow_count;
790
791char *current_progress;
792
793static UnsignedWide last_microseconds;
794
795static char *last_spin_file = "";
796
797static int last_spin_line;
798
799void
800warn_if_spin_delay (char *file, int line)
801{
802  long diff, ix;
803  UnsignedWide now;
804
805  Microseconds(&now);
806
807  diff = now.lo - last_microseconds.lo;
808
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)
815    {
816      if (diff >= 0)
817	{
818	  ix = diff >> bucket_power;
819	  if (ix >= 0 && ix < numbuckets && delay_counts != NULL)
820	    ++delay_counts[ix];
821	  else
822	    ++overflow_count;
823	}
824      else
825	fprintf (stderr, "raw diff is %ld (?)\n", diff);
826    }
827}
828
829void
830record_for_spin_delay (char *file, int line)
831{
832  Microseconds (&last_microseconds);
833  last_spin_file = file;
834  last_spin_line = line;
835}
836
837void
838mpw_start_progress (char *str, int n, char *file, int line)
839{
840  int i;
841  char *measure, *threshold;
842
843  if (!cursor_inited)
844    {
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')
850	{
851	  measure_spin = 1;
852	  if (strcmp (measure, "all") == 0)
853	    dump_spin_data = 1;
854	}
855      threshold = getenv ("SPIN_WARN_THRESHOLD");
856      if (threshold != NULL && threshold[0] != '\0')
857	warning_threshold = atol (threshold);
858      if (dump_spin_data)
859	{
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;
865	}
866    }
867  current_progress = str;
868
869  sys_nerr = errno_max ();
870
871  mpw_special_init (str);
872}
873
874void
875mpw_progress (int n)
876{
877  SpinCursor (32);
878}
879
880void
881mpw_progress_measured (int n, char *file, int line)
882{
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);
888}
889
890void
891mpw_end_progress (char *str, char *file, int line)
892{
893  long i, delay, count = 0, sum = 0, avgdelay, spinrate;
894  long curpower = 0, curgroup = 0;
895
896  /* Warn if it's been a while since the last spin.  */
897  if (measure_spin)
898    warn_if_spin_delay (file, line);
899
900  /* Dump all the nonzero delay counts and an approximation of the delay.  */
901  if (dump_spin_data && delay_counts != NULL)
902    {
903      for (i = 0; i < numbuckets; ++i)
904	{
905	  delay = (i + 1) * bucket_size;
906	  sum += delay_counts[i] * (i + 1);
907	  count += delay_counts[i];
908	  if (delay <= (1 << curpower))
909	    {
910	      curgroup += delay_counts[i];
911	    }
912	  else
913	    {
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;
925	    }
926	}
927      if (count > 0)
928	{
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);
933	}
934    }
935}
936
937#ifdef PROGRESS_TEST
938
939/* Test program.  */
940
941main ()
942{
943  int i, j;
944  double x = 1.0, y = 2.4;
945  long start = Microseconds (), tm;  FIXME
946
947  START_PROGRESS ("hi", 0);
948
949  for (i = 0; i < 1000; ++i)
950    {
951      PROGRESS (1);
952
953      for (j = 0; j < (i * 100); ++j)
954	{
955	  x += (x * y) / j;
956	}
957    }
958
959  END_PROGRESS ("hi");
960
961  tm = Microseconds () - start;
962
963  printf ("Total time is %d.%d secs\n", tm / 1000000, tm % 1000000);
964}
965
966#endif
967
968#ifdef USE_MW_HEADERS
969/* Empty definitions for Metrowerks' SIOUX console library. */
970
971#ifndef __CONSOLE__
972#include <console.h>
973#endif
974
975short
976InstallConsole(short fd)
977{
978#pragma unused (fd)
979	return 0;
980}
981
982void
983RemoveConsole(void)
984{
985}
986
987long
988WriteCharsToConsole(char *buf, long n)
989{
990#pragma unused (buf, n)
991	return 0;
992}
993
994long ReadCharsFromConsole(char *buf, long n)
995{
996#pragma unused (buf, n)
997	return 0;
998}
999
1000extern char *
1001__ttyname(long fd)
1002{
1003	static char *__devicename = "null device";
1004
1005	if (fd >= 0 && fd <= 2)
1006	  return (__devicename);
1007	return NULL;
1008}
1009
1010#endif
1011