home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / libg++-2.7.1-base.tgz / libg++-2.7.1-src.tar / fsf / libg++ / libiberty / mpw.c < prev    next >
C/C++ Source or Header  |  1995-11-12  |  20KB  |  997 lines

  1. /* MPW-Unix compatibility library.
  2.    Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
  3.  
  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.
  9.  
  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.
  14.  
  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.  */
  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. /* Initialize to 0 at first, then set to errno_max() later.  */
  35.  
  36. int sys_nerr = 0;
  37.  
  38. int DebugPI = 0;
  39.  
  40. void mpw_abort (void);
  41.  
  42. void
  43. mpwify_filename(char *unixname, char *macname)
  44. {
  45.   int i, j, in_middle, terminate = 0;
  46.  
  47.   /* (should truncate 255 chars from end of name, not beginning) */
  48.   if (strlen (unixname) > 255)
  49.     {
  50.       fprintf (stderr, "Pathname \"%s\" is too long for Macs, truncating\n",
  51.            unixname);
  52.       terminate = 1;
  53.     }
  54.   /* Abs Unix path to abs Mac path. */
  55.   if (*unixname == '/')
  56.     {
  57.       if (strncmp (unixname, "/tmp/", 6) == 0)
  58.     {
  59.       /* A temporary name, make a more Mac-flavored tmpname. */
  60.       /* A better choice would be {Boot}Trash:foo, but that would
  61.          require being able to identify the boot disk's and trashcan's
  62.          name.  Another option would be to have an env var, so user
  63.          can point it at a ramdisk. */
  64.       strncpy (macname, unixname, 255);
  65.       if (terminate)
  66.         macname[255] = '\0';
  67.       macname[0] = ':';
  68.       macname[4] = '_';
  69.     }
  70.       else
  71.     {
  72.       /* Assume that the leading component is a valid disk name. */
  73.       strncpy (macname, unixname + 1, 255);
  74.     }
  75.     }
  76.   else
  77.     {
  78.       /* If this is a "Unix-only" pathname, assume relative. */
  79.       if (strchr (unixname, '/') && ! strchr (unixname, ':'))
  80.     {
  81.       macname[0] = ':';
  82.       strncpy (macname + 1, unixname, 255);
  83.     }
  84.       else
  85.     {
  86.       /* Otherwise copy it verbatim. */
  87.       /* ... but if of the form ":/foo", lose the extra colon;
  88.          the slash will be made into a colon shortly. */
  89.       if (unixname[0] == ':' && unixname[1] == '/')
  90.         ++unixname;
  91.       strncpy (macname, unixname, 255);
  92.     }
  93.     }
  94.   if (terminate)
  95.     macname[255] = '\0';
  96.   for (i = 0; macname[i] != '\0'; ++i)
  97.     {
  98.       if (macname[i] == '/')
  99.     macname[i] = ':';
  100.     }
  101.   in_middle = 0;
  102.   j = 0;
  103.   for (i = 0; macname[i] != '\0'; ++i)
  104.     {
  105.       /* We're in the middle of the name when a char is not a colon. */
  106.       if (macname[i] != ':')
  107.     in_middle = 1;
  108.       /* Copy chars verbatim, *unless* the char is the first of a pair
  109.      of colons in the middle of a pathname. */
  110.       if (!(in_middle && macname[i] == ':' && macname[i+1] == ':'))
  111.     macname[j++] = macname[i];
  112.     }
  113.   macname[j] = '\0';
  114.   /* If we have a trailing ":.", make it into a ":". */
  115.   if (j >= 2 && macname[j-2] == ':' && macname[j-1] == '.')
  116.     macname[j-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.  
  126. char *
  127. mpw_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.  
  148. char *
  149. mpw_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.  
  168. FILE *
  169. mpw_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.  
  219. char zeros[ZEROBLKSIZE];
  220.  
  221. int
  222. mpw_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.  
  250. int
  251. mpw_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.  
  262. int
  263. mpw_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.  
  274. int
  275. link ()
  276. {
  277.   fprintf (stderr, "link not available!\n");
  278.   mpw_abort ();
  279. }
  280.  
  281. int
  282. fork ()
  283. {
  284.   fprintf (stderr, "fork not available!\n");
  285.   mpw_abort ();
  286. }
  287.  
  288. int
  289. vfork ()
  290. {
  291.   fprintf (stderr, "vfork not available!\n");
  292.   mpw_abort ();
  293.   return (-1);
  294. }
  295.  
  296. int
  297. pipe (int *fd)
  298. {
  299.   fprintf (stderr, "pipe not available!\n");
  300.   mpw_abort ();
  301.   return (-1);
  302. }
  303.  
  304. #ifndef USE_MW_HEADERS
  305. int
  306. execvp (char *file, char **argv)
  307. {
  308.   fprintf (stderr, "execvp not available!\n");
  309.   mpw_abort ();
  310.   return (-1);
  311. }
  312.  
  313. int
  314. execv (char *path, char **argv)
  315. {
  316.   fprintf (stderr, "execv not available!\n");
  317.   mpw_abort ();
  318.   return (-1);
  319. }
  320. #endif
  321.  
  322. int
  323. kill (int pid, int sig)
  324. {
  325.   fprintf (stderr, "kill not available!\n");
  326.   mpw_abort ();
  327.   return (-1);
  328. }
  329.  
  330. int
  331. wait (int *status)
  332. {
  333.   *status = 0;
  334.   return 0;
  335. }
  336.  
  337. #ifndef USE_MW_HEADERS
  338. int
  339. sleep (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.  
  355. void
  356. putenv (char *str)
  357. {
  358.   /* The GCC driver calls this to do things for collect2, but we
  359.      don't care about collect2. */
  360. }
  361.  
  362. int
  363. chmod (char *path, int mode)
  364. {
  365.   /* Pretend it was all OK. */
  366.   return 0;
  367. }
  368.  
  369. #ifndef USE_MW_HEADERS
  370. int
  371. getuid ()
  372. {
  373.   /* One value is as good as another... */
  374.   return 0;
  375. }
  376.  
  377. int
  378. getgid ()
  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.  
  389. void
  390. mpw_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.  
  407. int
  408. getrusage (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.  
  420. int
  421. sbrk ()
  422. {
  423.   return 0;
  424. }
  425.  
  426. #ifndef USE_MW_HEADERS
  427. int
  428. isatty (int fd)
  429. {
  430.   return 0;
  431. }
  432.  
  433. /* This is inherited from Timothy Murray's Posix library. */
  434.  
  435. #include "utime.h"
  436.  
  437. int
  438. utime (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.  
  470. int
  471. mkdir (char *path, int mode)
  472. {
  473.   errno = ENOSYS;
  474.   return -1;
  475. }
  476.  
  477. int
  478. rmdir ()
  479. {
  480.   errno = ENOSYS;
  481.   return -1;
  482. }
  483. #endif
  484.  
  485. chown ()
  486. {
  487.   errno = ENOSYS;
  488.   return -1;
  489. }
  490.  
  491. char *myenviron[] = {NULL};
  492.  
  493. char **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.  
  503. extern int __uid, __gid;
  504.  
  505. int __uid = 0;
  506. int __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.  
  515. static 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.  
  577. int
  578. stat (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.  
  602. int
  603. fstat (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)
  613.     fprintf (stderr, "# fstat (%d, %x)", fd, buf);
  614.   PROGRESS (1);
  615.   pathname[0] = 0;
  616. #ifdef FIOFNAME
  617.   /* Use an MPW-specific ioctl to get the pathname associated with
  618.      the file descriptor.  */
  619.   ioctl (fd, FIOFNAME, (long *) pathname); 
  620. #else
  621.   you lose
  622. #endif
  623.   if (DebugPI)
  624.     fprintf (stderr, " (name is %s)", pathname);
  625.   dirid = 0L /* fcb.ioFCBParID */ ;
  626.   rslt = _stat ((char *) pathname, dirid, buf);
  627.   errnum = errno;
  628.   if (DebugPI)
  629.     {
  630.       fprintf (stderr, " -> %d", rslt);
  631.       if (rslt != 0)
  632.     fprintf (stderr, " (errno is %d)", errnum);
  633.       fprintf (stderr, "\n");
  634.       fflush (stderr);
  635.     }
  636.   if (rslt != 0)
  637.     errno = errnum;
  638.   return rslt;
  639. }
  640.  
  641. #endif /* n USE_MW_HEADERS */
  642.  
  643. chdir ()
  644. {
  645.   errno = ENOSYS;
  646.   return (-1);
  647. }
  648.  
  649. char *
  650. getcwd (char *buf, int size)
  651. {
  652.   if (buf == NULL)
  653.     buf = (char *) malloc (size);
  654.   strcpy(buf, ":");
  655.   return buf;
  656. }
  657.  
  658. /* This should probably be more elaborate for MPW. */
  659.  
  660. char *
  661. getpwd ()
  662. {
  663.   return ":";
  664. }
  665.  
  666. int
  667. mpw_open (char *filename, int arg2, int arg3)
  668. {
  669. #undef open
  670.  
  671.   char tmpname[256];
  672.  
  673.   mpwify_filename (filename, tmpname);
  674.   return open (tmpname, arg2);
  675. }
  676.  
  677. int
  678. mpw_access (char *filename, unsigned int cmd)
  679. {
  680. #undef access
  681.  
  682.   int rslt, errnum = 0;
  683.   struct stat st;
  684.   char tmpname[256];
  685.  
  686.   mpwify_filename (filename, tmpname);
  687.   if (DebugPI)
  688.     fprintf (stderr, "# mpw_access (%s, %d)", tmpname, cmd);
  689.   if (cmd & R_OK || cmd & X_OK)
  690.     {
  691.       rslt = stat (tmpname, &st);
  692.       errnum = errno;
  693.       if (rslt >= 0)
  694.     {
  695.       if (((st.st_mode & 004 == 0) && (cmd & R_OK))
  696.           || ((st.st_mode & 002 == 0) && (cmd & W_OK))
  697.           || ((st.st_mode & 001 == 0) && (cmd & X_OK)))
  698.         {
  699.           rslt = -1;
  700.           errnum = EACCES;
  701.         }
  702.     }
  703.     }
  704.   if (DebugPI)
  705.     {
  706.       fprintf (stderr, " -> %d", rslt);
  707.       if (rslt != 0)
  708.     fprintf (stderr, " (errno is %d)", errnum);
  709.       fprintf (stderr, "\n");
  710.     }
  711.   if (rslt != 0)
  712.     errno = errnum;
  713.   return rslt;
  714. }
  715.  
  716. /* The MPW library creat() has no mode argument. */
  717.  
  718. int
  719. mpw_creat (char *path, /* mode_t */ int mode)
  720. {
  721. #undef creat
  722.  
  723. #ifdef USE_MW_HEADERS
  724.   return creat (path, mode);
  725. #else
  726.   return creat (path);
  727. #endif
  728. }
  729.  
  730. /* This is a hack to get control in an MPW tool before it crashes the
  731.    machine.  */
  732.  
  733. mpw_special_init (name)
  734.      char *name;
  735. {
  736.   if (strstr (name, "DEBUG"))
  737.     DebugStr("\pat beginning of program");
  738. }
  739.  
  740. static int current_umask;
  741.  
  742. int
  743. umask(int mask)
  744. {
  745.   int oldmask = current_umask;
  746.  
  747.   current_umask = mask;
  748.   return oldmask;
  749. }
  750.  
  751. /* Cursor-spinning stuff that includes metering of spin rate and delays.  */
  752.  
  753. /* Nonzero when cursor spinning has been set up properly.  */
  754.  
  755. int cursor_inited;
  756.  
  757. /* Nonzero if spin should be measured and excessive delays reported.  */
  758.  
  759. int measure_spin;
  760.  
  761. /* Nonzero if spin histogram and rate data should be written out.  */
  762.  
  763. int dump_spin_data;
  764.  
  765. long warning_threshold = 400000;
  766.  
  767. long bucket_size = 1024;
  768.  
  769. long bucket_power = 10;
  770.  
  771. long numbuckets = 300;
  772.  
  773. int *delay_counts;
  774.  
  775. int overflow_count;
  776.  
  777. char *current_progress;
  778.  
  779. static UnsignedWide last_microseconds;
  780.  
  781. static char *last_spin_file = "";
  782.  
  783. static int last_spin_line;
  784.  
  785. void
  786. warn_if_spin_delay (char *file, int line)
  787. {
  788.   long diff, ix;
  789.   UnsignedWide now;
  790.  
  791.   Microseconds(&now);
  792.  
  793.   diff = now.lo - last_microseconds.lo;
  794.  
  795.   if (diff > warning_threshold)
  796.     fprintf (stderr, "# %s: %ld.%06ld sec delay getting from %s:%d to %s:%d\n",
  797.          (current_progress ? current_progress : ""),
  798.          diff / 1000000, diff % 1000000,
  799.          last_spin_file, last_spin_line, file, line);
  800.   if (dump_spin_data)
  801.     {
  802.       if (diff >= 0)
  803.     {
  804.       ix = diff >> bucket_power;
  805.       if (ix >= 0 && ix < numbuckets && delay_counts != NULL)
  806.         ++delay_counts[ix];
  807.       else
  808.         ++overflow_count;
  809.     }
  810.       else
  811.     fprintf (stderr, "raw diff is %ld (?)\n", diff);
  812.     }
  813. }
  814.  
  815. void
  816. record_for_spin_delay (char *file, int line)
  817. {
  818.   Microseconds (&last_microseconds);
  819.   last_spin_file = file;
  820.   last_spin_line = line;
  821. }
  822.  
  823. void
  824. mpw_start_progress (char *str, int n, char *file, int line)
  825. {
  826.   int i;
  827.   char *measure, *threshold;
  828.  
  829.   if (!cursor_inited)
  830.     {
  831.       InitCursorCtl (nil);
  832.       cursor_inited = 1;
  833.       record_for_spin_delay (file, line);
  834.       measure = getenv ("MEASURE_SPIN");
  835.       if (measure != NULL && measure[0] != '\0')
  836.     {
  837.       measure_spin = 1;
  838.       if (strcmp (measure, "all") == 0)
  839.         dump_spin_data = 1;
  840.     }
  841.       threshold = getenv ((const char *) "SPIN_WARN_THRESHOLD");
  842.       if (threshold != NULL && threshold[0] != '\0')
  843.     warning_threshold = atol (threshold);
  844.       if (dump_spin_data)
  845.     {
  846.       if (delay_counts == NULL)
  847.         delay_counts = (int *) malloc (numbuckets * sizeof (int));
  848.       for (i = 0; i < numbuckets; ++i)
  849.         delay_counts[i] = 0;
  850.       overflow_count = 0;
  851.     }
  852.     }
  853.   current_progress = str;
  854.  
  855.   sys_nerr = errno_max ();
  856.  
  857.   mpw_special_init (str);
  858. }
  859.  
  860. void
  861. mpw_progress (int n)
  862. {
  863.   SpinCursor (32);
  864. }
  865.  
  866. void
  867. mpw_progress_measured (int n, char *file, int line)
  868. {
  869.   if (measure_spin)
  870.     warn_if_spin_delay (file, line);
  871.   SpinCursor (32);
  872.   if (measure_spin)
  873.     record_for_spin_delay (file, line);
  874. }
  875.  
  876. void
  877. mpw_end_progress (char *str, char *file, int line)
  878. {
  879.   long i, delay, count = 0, sum = 0, avgdelay, spinrate;
  880.   long curpower = 0, curgroup = 0;
  881.  
  882.   /* Warn if it's been a while since the last spin.  */
  883.   if (measure_spin)
  884.     warn_if_spin_delay (file, line);
  885.  
  886.   /* Dump all the nonzero delay counts and an approximation of the delay.  */
  887.   if (dump_spin_data && delay_counts != NULL)
  888.     {
  889.       for (i = 0; i < numbuckets; ++i)
  890.     {
  891.       delay = (i + 1) * bucket_size;
  892.       sum += delay_counts[i] * (i + 1);
  893.       count += delay_counts[i];
  894.       if (delay <= (1 << curpower))
  895.         {
  896.           curgroup += delay_counts[i];
  897.         }
  898.       else
  899.         {
  900.           if (curgroup > 0)
  901.         fprintf (stderr,
  902.              "# %s: %d delays between %ld.%06ld and %ld.%06ld sec\n",
  903.              (str ? str : ""),
  904.              curgroup,
  905.              (1 << curpower) / 1000000,
  906.              (1 << curpower) % 1000000,
  907.              (1 << (curpower + 1)) / 1000000,
  908.              (1 << (curpower + 1)) % 1000000);
  909.           ++curpower;
  910.           curgroup = 0;
  911.         }
  912.     }
  913.       if (count > 0)
  914.     {
  915.       avgdelay = (sum * bucket_size) / count;
  916.       spinrate = 1000000 / avgdelay;
  917.       fprintf (stderr, "# %s: Average spin rate is %d times/sec\n",
  918.            (str ? str : ""), spinrate);
  919.     }
  920.     }
  921. }
  922.  
  923. #ifdef PROGRESS_TEST
  924.  
  925. /* Test program.  */
  926.  
  927. main ()
  928. {
  929.   int i, j;
  930.   double x = 1.0, y = 2.4;
  931.   long start = Microseconds (), tm;  FIXME
  932.  
  933.   START_PROGRESS ("hi", 0);
  934.  
  935.   for (i = 0; i < 1000; ++i)
  936.     {
  937.       PROGRESS (1);
  938.  
  939.       for (j = 0; j < (i * 100); ++j)
  940.     {
  941.       x += (x * y) / j;
  942.     }
  943.     }
  944.   
  945.   END_PROGRESS ("hi");
  946.   
  947.   tm = Microseconds () - start;
  948.  
  949.   printf ("Total time is %d.%d secs\n", tm / 1000000, tm % 1000000);
  950. }
  951.  
  952. #endif
  953.  
  954. #ifdef USE_MW_HEADERS
  955. /* Empty definitions for Metrowerks' SIOUX console library. */
  956.  
  957. #ifndef __CONSOLE__
  958. #include <console.h>
  959. #endif
  960.  
  961. short
  962. InstallConsole(short fd)
  963. {
  964. #pragma unused (fd)
  965.     return 0;
  966. }
  967.  
  968. void
  969. RemoveConsole(void)
  970. {
  971. }
  972.  
  973. long
  974. WriteCharsToConsole(char *buf, long n)
  975. {
  976. #pragma unused (buf, n)
  977.     return 0;
  978. }
  979.  
  980. long ReadCharsFromConsole(char *buf, long n)
  981. {
  982. #pragma unused (buf, n)
  983.     return 0;
  984. }
  985.  
  986. extern char *
  987. __ttyname(long fd)
  988. {
  989.     static char *__devicename = "null device";
  990.  
  991.     if (fd >= 0 && fd <= 2)
  992.       return (__devicename);
  993.     return NULL;
  994. }
  995.  
  996. #endif
  997.