home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / dskutl / free151.arc / FREE.C < prev    next >
Text File  |  1989-02-08  |  28KB  |  596 lines

  1. /****************************************************************************
  2.  
  3.     FREE   : free.c checks disk free space, with options
  4.  
  5.     Author : Howard S. Kapustein
  6.  
  7.     Program, Source, Documentation Copyright (c) 1988,1989 all rights reserved.
  8.  
  9.     Compiled code may be used in your library, and linked to your programs
  10.     BUT, the source code nor a library based on the compiled modules may be
  11.     commercially distributed.
  12.     I retain all rights to the source (this is all lawyer talk. I just don't
  13.     want someone making money off my efforts.)
  14.     No donation is expected. If you like FREE, send me a picture postcard
  15.     or, if you must, $5. I am a poor starving college student and will 
  16.     gladly accept all comments, questions, suggestions and/or donations.
  17.  
  18.     For a quick start try   FREE -?
  19.  
  20.     I know this is a pretty simple program. There are plenty of versions
  21.     of free disk space programs in the public domain, but none suited my 
  22.     needs. I needed 3 features besides the standard disk space free:
  23.  
  24.         1) Check more than 1 drive per run
  25.         2) Optionally check all drives
  26.         3) Optionally wait for a keypress after execution for use
  27.            with DESQview (tm)
  28.  
  29.     FREE.C, FREE.EXE and FREE.DOC must be distributed together.
  30.  
  31.     FREE was compiled with Turbo C (tm) 2.0 on an IBM PC with the Tiny
  32.     memory model optimized on Speed and all optimizations turned on
  33.     (Register and Jump optimization).
  34.  
  35.     Release notes:      2-2-88  Version 1.0 - Initial release
  36.  
  37.                         2-22-88 Version 1.1
  38.                                   - checks for drive ready before checking
  39.                                     free space
  40.                                   - skips drive B: only 1 drive present
  41.                                     (B: is a logical drive, not a physical
  42.                                     drive)
  43.                                   - optional wait after execution can now
  44.                                     wait for a specific key (specify its
  45.                                     decimal scan code, only valid for keys
  46.                                     1-255 (no extended keys i.e. Alt-C, etc.)
  47.                                   - fixed a bug with the strtocomma() function.
  48.                                     If a 4-digit number (i.e. 9216) was passed
  49.                                     to it it would not insert any commas
  50.                                   - I no longer make FREE.COM, for some reason
  51.                                     under DESQview FREE.COM 1.0 still needed
  52.                                     approx 70K, so until I can figure out how
  53.                                     to get FREE to use appropriate only the 11K
  54.                                     or so it needs, I'm not going to bother
  55.                                     playing with COM files
  56.  
  57.                         2-29-88 Version 1.2
  58.                                   - recompiled under Turbo C 1.5 (it only took
  59.                                     them 6 weeks instead of the 2-3 it should
  60.                                     have...) I turned on ALL optimizations
  61.                                     (speed, use regs, optimize regs and jumps.)
  62.                                     Previous versions were only compiled with
  63.                                     speed and use registers.
  64.                                   - modified the parsing for more coherent
  65.                                     program flow
  66.                                   - made waitforkey() after having extensive
  67.                                     difficulties getting the -w option to work.
  68.                                     This seems to have fixed the runtime bugs
  69.                                     I was getting after recompiling with TC 1.5
  70.                                   - changed the HEAP and STACK size so it will
  71.                                     run with less memory
  72.                                   - removed some needless comments left over
  73.                                     from old coding
  74.                                   - include a DESQview .DVP file for use with
  75.                                     DESQview.
  76.  
  77.                         3-19-88 Version 1.3
  78.                                   - fixed the bug with multiple drives
  79.                                     specified on the command line. It seems
  80.                                     that TC 1.5 DOES NOT let you use the
  81.                                     generic argc and argv as in
  82.                                     main(int argc, char *argv[]) Now Borland
  83.                                     wants you to use their global variables
  84.                                     (extern int argc, etc.). Nice of them not
  85.                                     to mention the fact that the old method
  86.                                     no longer works.
  87.  
  88.                         3-23-88 Version 1.31  -  Internal release only
  89.                                   - if you try to do a   FREE -   with no
  90.                                     parms free would check the default drive.
  91.                                     This has been fixed to display the help
  92.                                     (technically, doing   FREE -   is wrong)
  93.                                   - version 1.2 allowed -w[n] option to check
  94.                                     for extended key codes, but I forgot to
  95.                                     document it. Use this formula to get the
  96.                                     value for the wait option:
  97.  
  98.                                         extended * 256 + scancode
  99.  
  100.                                     where extended is 0 if False and 1 if True
  101.                                     and scancode is the scan code returned by
  102.                                     the key (refer to one of Norton's books
  103.                                     or some other reference guide for a list
  104.                                     of IBM extended scan codes.)
  105.  
  106.                         4-10-88 Version 1.32  -  Internal release only
  107.                                   - I removed a printf() left over from
  108.                                     debugging
  109.                                   - included info about contacting me during
  110.                                     the school year
  111.  
  112.                          5-6-88 Version 1.33  - Internal release only
  113.                                   - included % free
  114.  
  115.                         5-11-88 Version 1.34  - Internal release only
  116.                                   - fixed -w option, was skipping past
  117.                                     1st digit of keycode ([n])
  118.                                   - also, discovered the problem with the
  119.                                     -w option. Compiled w/full optimization,
  120.                                     the waitkey value was stored in a
  121.                                     register which was destroyed during the
  122.                                     absread(). This is a problem of the
  123.                                     TC library itself. I don't remember
  124.                                     who it was who discovered this flaw, but
  125.                                     if you drop me a line I'll update these
  126.                                     docs. He has notified Borland, so
  127.                                     hopefully the next TC will have this bug
  128.                                     fixed (if not sooner, but I'm not
  129.                                     holding my breath.) Thank you for the
  130.                                     debugging, it gave me many a restless
  131.                                     hour. DO NOT compile FREE with register
  132.                                     variables on, or unknown results may
  133.                                     (and probably will) occur
  134.  
  135.                         11-9-88 Version 1.4
  136.                                   - recompiled under TC 2.0 in Small model,
  137.                                     Merge duplicate strings On, Standard
  138.                                     stack frame Off, and all Debug info
  139.                                     left out. Couldn't compile under Tiny
  140.                                     (Cannot generate COM file: segment-
  141.                                     relocatable items present) probably because
  142.                                     of the FP emulator - see below
  143.                                   - added optional message if wait for key
  144.                                   - added -f and -n (search only floppies,
  145.                                     non-floppies). The -x option is equivalent
  146.                                     to -fn
  147.                                   - added -e option (full error display, useful
  148.                                     if you want to see any errors during a -f,
  149.                                     -n, or -x run). This setting is turned on
  150.                                     automatically for single mode (if -f, -n
  151.                                     and -x are not specified)
  152.                                   - minor code tinkering for aesthetics
  153.                                   - DESQview 2.2 .DVP file
  154.                                   - for some reason, FREE running under
  155.                                     DV 2.2 now requires 65K. According to my
  156.                                     estimates, it needs anywhere from 44K to
  157.                                     66K. I think this is related to the Tiny
  158.                                     problem (inability to make .COM file). If
  159.                                     anyone can figure out why, please let me
  160.                                     know. Without the FP emulator, I can't
  161.                                     display a % free, but requiring 65K to get
  162.                                     a free disk space count is ridiculous
  163.  
  164.                          2-7-89 Version 1.5
  165.                                   - new option -N, Network compatibility,
  166.                                     skips the absolute disk read before
  167.                                     checking free space. If a free space
  168.                                     request is attempted on drive A: with
  169.                                     no floppy in the drive, DOS bombs out to
  170.                                     the Abort,Ignore,Retry message. Turning on
  171.                                     this option should allow execution on
  172.                                     drives not physically available (networks,
  173.                                     those accessed via device drivers, etc.)
  174.                                   - changed option letter -N to -H to allow
  175.                                     network compatibility. The -H option
  176.                                     will scan non-floppy drives (generally
  177.                                     fixed disks, C: and up.)
  178.                                   - no longer compiled with the FP emulation,
  179.                                     the % free real number is now achieved via
  180.                                     integers. Without the FP emulation FREE can
  181.                                     be compiled with the Tiny memory model.
  182.                                   - added check for DOS environment variable
  183.                                     FREEOPT for options
  184.                                   - changed the Heap size to 4K and the stack
  185.                                     to 1K
  186.                                   - changed the command line parsing to a
  187.                                     separate function, and no longer copy each
  188.                                     *_argv before processing it. The pointer
  189.                                     char *temp replace the older char temp[30]
  190.                                     for several reasons: instead of a strcpy()
  191.                                     we can just do a temp = *_argv, and if an
  192.                                     argument string was longer than 29
  193.                                     characters, we would have problems copying
  194.                                     to temp[30]. Pointers make life simpler
  195.                                   - you can now check multiple drives via
  196.                                     command line switch, and specific drives
  197.                                     via drive letter, in the same run (i.e.
  198.                                     FREE /F B: checks all floppy drives and
  199.                                     drive B:)
  200.                                   - IMPORTANT! FREE is now distributed as a
  201.                                     .COM file! If you are using an earlier
  202.                                     verison with a .EXE extension, make sure
  203.                                     you delete it otherwise DOS may not find
  204.                                     the proper file to run. See above regarding
  205.                                     FP emulation as to why we're back to a
  206.                                     .COM file.
  207.                                Note: I've enclosed the substitute division
  208.                                      function within a conditional #if...#endif
  209.                                      set so you can recompile easily with the
  210.                                      emulation or 8087 library and not have
  211.                                      unnecessary code. If MATH_FP87 or MATH_EMU
  212.                                      is #defined, the rdiv() function will not
  213.                                      be compiled. Otherwise, no math library
  214.                                      will be linked in, and the final .COM
  215.                                      will only need ~15K to run.
  216.  
  217.                          2-8-89 Version 1.51
  218.                                   - if no command line arguments given, only
  219.                                     the current drive would be checked, even
  220.                                     if FREEOPT=X. Fixed
  221.  
  222.  
  223.     I can be reached at:
  224.  
  225.     Home:   Howard S. Kapustein
  226.             1695 Barbara Lane               
  227.             East Meadow, New York 11554  
  228.             Phone: (516) 481-9612           
  229.  
  230.     College (until 5-19-89):    Howard S. Kapustein
  231.                                 404 Davison
  232.                                 Rensselaer Polytechnic Institute
  233.                                 Troy, NY 12180
  234.                                 (518) 276-7381
  235.  
  236.     Modem:  The BOSS (201) 568-7293  ***Support BBS
  237.             Software Society (201) 729-7410
  238.             Computer Connection (202) 547-2008
  239.             GEnie
  240.  
  241.         Until graduation (May 19, 1989) please try to contact me
  242.     at college. If you call my home while I'm in Troy you're not
  243.     likely to get much information (my parents aren't exactly
  244.     computer people.)
  245.  
  246.  
  247. ****************************************************************************/
  248.  
  249. #include <ctype.h>
  250. #include <bios.h>
  251. #include <dos.h>
  252. #include <string.h>
  253. #include <stdlib.h>
  254. #include <stdio.h>
  255. #include <dir.h>
  256. typedef struct rdiv_t {
  257.             long rint;              /* real - integer */
  258.             long rfrac;             /* real - fraction */
  259.         };
  260.  
  261. #define TRUE        1
  262. #define FALSE       0
  263. #define boolean     char
  264. #define BUFF_LEN    512                /* buffer length for disk read */
  265. #define FLOPPY      0x10
  266. #define FIXED       0x20
  267. #define ALLDRIVES   (FIXED|FLOPPY)
  268. #define isBlogical()    ((biosequip()&0xB0)?FALSE:TRUE)
  269.  
  270. typedef struct options {
  271.         char scandrives;
  272.         boolean network;
  273.         boolean errormsg;
  274.         boolean wait;
  275.         boolean waitmsg;
  276.         int waitkey;
  277.     };
  278.  
  279. /* Global variables */
  280. struct options flag;
  281.  
  282. /* function prototypes */
  283. void main();                                /* prevents compiler warning */
  284. void check(int drive);                      /* actual free space check and output */
  285. char *strtocomma(char *source);             /* convert a string to xx,xxx,xxx format */
  286. void help(void);                            /* help */
  287. void waitforkey(int keycode, boolean msg);  /* wait for keypress when done */
  288. void parsecommandline(char *cmdline);       /* parse a command line */
  289. void bye(int errcode);                      /* generalized quit routine */
  290. struct rdiv_t rdiv(long int numer, long int denom, int precision);  /* real division without FP emulator library */
  291.  
  292.  
  293. void main()
  294. {
  295.     extern int _argc;
  296.     extern char **_argv;
  297.     extern unsigned _heaplen, _stklen;
  298.     char *temp;
  299.     boolean Blogical;
  300.     int i, floppies;
  301.  
  302.     _heaplen = 4096u;        /* heap  = 4K */
  303.     _stklen = 1024u;         /* stack = 1K */
  304.  
  305.     _argc--;
  306.     _argv++;
  307.  
  308.     flag.scandrives = FALSE;                    /* initialize settings */
  309.     flag.network = FALSE;
  310.     flag.errormsg = FALSE;
  311.     flag.wait = FALSE;
  312.     flag.waitmsg = TRUE;
  313.     flag.waitkey = 0;
  314.  
  315.     if ((temp = getenv("FREEOPT")) != NULL)
  316.         parsecommandline(temp);
  317.  
  318.     if (_argc > 0)                      /* check for options */
  319.         for (temp = *_argv; (temp[0] == '-') || (temp[0] == '/'); temp = *_argv) {
  320.             if (temp[1] == '\0')
  321.                 help();
  322.             parsecommandline(temp+1);
  323.             _argc--;
  324.             _argv++;
  325.             if (_argc == 0)
  326.                 break;
  327.         }
  328.  
  329.     if (flag.scandrives) {               /* check multiple drives? */
  330.         floppies = ((biosequip() & 0xB0) >> 6) + 1;
  331.         Blogical = (floppies > 1) ? FALSE : TRUE;
  332.         if (flag.scandrives & FLOPPY)        /* check floppy drives? */
  333.             for (i=1; i<=floppies; i++)     /* check floppies */
  334.                 if (!(i==2 && Blogical))
  335.                     check(i);
  336.         if (flag.scandrives & FIXED) {       /* check non-floppies? */
  337.             i = Blogical ? 3 : ++floppies;
  338.             for (; i<=26; i++)              /* check hard drives */
  339.                 check(i);
  340.         }
  341.     }
  342.  
  343.     if ((_argc == 0) && !flag.scandrives) {      /* ok, just parms (no drive specified) */
  344.         flag.errormsg = TRUE;    /* force error message display for non-multiple mode */
  345.         check(0);
  346.         if (flag.wait)
  347.             waitforkey(flag.waitkey,flag.waitmsg);
  348.         bye(3);
  349.     }
  350.  
  351.     flag.errormsg = TRUE;    /* force error message display for non-multiple mode */
  352.     for (; _argc>0; _argc--, _argv++) {
  353.         strcpy(temp,*_argv);
  354.         if (!isalpha(temp[0]=toupper(temp[0])))
  355.             printf("Invalid drive: %c\n",temp[0]);
  356.         else
  357.             check(temp[0]-64);
  358.     }
  359.  
  360.     if (flag.wait)
  361.         waitforkey(flag.waitkey,flag.waitmsg);
  362. }
  363.  
  364.  
  365. void check(int drive)
  366. {
  367.     extern struct options flag;
  368.     struct dfree df;
  369.     char size[30];
  370.     unsigned char buffer[BUFF_LEN];
  371.     long avail, total;
  372. #if defined(MATH_FP87) || defined(MATH_EMU)     /* using 80x87 or Emulation math library */
  373.     double percent;
  374. #else                                           /* using No math library */
  375.     struct rdiv_t percent;
  376. #endif
  377.  
  378.     if (drive==0)
  379.         drive = getdisk() + 1;
  380.     if (!flag.network && absread(drive-1,1,0,buffer)==-1) {      /* error */
  381.         if (flag.errormsg)
  382.             printf("Drive %c: is not available (drive not ready)\n",drive+64);
  383.     } else {
  384.         getdfree(drive,&df);
  385.         if (df.df_sclus == (unsigned)(-1)) {            /* error */
  386.             if (flag.errormsg)
  387.                 printf("Drive %c: is not available (can't get free space)\n",drive+64);
  388.         } else {
  389.             avail = (long)df.df_avail * df.df_bsec * df.df_sclus;
  390.             total = (long)df.df_total * df.df_bsec * df.df_sclus;
  391.             ltoa(avail,size,10);
  392. #if defined(MATH_FP87) || defined(MATH_EMU)     /* using 80x87 or Emulation math library */
  393.             percent = (avail * 100.0) / ((long)df.df_total * df.df_bsec * df.df_sclus);
  394.             printf("Drive %c: has %s bytes free (%.2lf %%)\n",drive+64,strtocomma(size),percent);
  395. #else                                           /* using No math library */
  396.             percent = rdiv(avail,total,4);
  397.             printf("Drive %c: has %s bytes free (%ld.%02ld %%)\n",drive+64,strtocomma(size),percent.rint*100+percent.rfrac/100,percent.rfrac%100);
  398. #endif
  399.         }
  400.     }
  401. }
  402.  
  403.  
  404. char *strtocomma(char *source)    /* convert a string to xx,xxx,xxx format */
  405. {
  406.     char temp[80],*t,*s;
  407.     int i,len;
  408.  
  409.     len = strlen(source) * 4/3 + 1;
  410.     strcpy(temp,"");
  411.     if ((len = strlen(source)) <= 3)
  412.         strcpy(temp,source);
  413.     else
  414.         for (i=len, t=temp, s=source; i>0; i--) {
  415.             if ((i%3 == 0) && (i != len))
  416.                 *t++ = ',';
  417.            *t++ = *s++;
  418.         }
  419.     *t = '\0';
  420.     strcpy(source,temp);
  421.     return(source);
  422. }
  423.  
  424.  
  425. void help(void)
  426. {
  427.     fputs("FREE     Free disk space      Version 1.51     2-8-89               FREE/?\n",stdout);
  428.     fputs("Copyright (c) 1988,1989 Howard Kapustein. All Rights Reserved      for help\n\n",stdout);
  429.     fputs("Usage: FREE [options] [drive] [drive] [drive] ...\n",stdout);
  430.     fputs("Options are:\n",stdout);
  431.     fputs("        -e = display all error messages      -f = check all floppy drives\n",stdout);
  432.     fputs("        -n = network compatibility           -h = check all non-floppy drives\n",stdout);
  433.     fputs("  -w[0][n] = wait for keypress when done     -x = check all drives\n",stdout);
  434.     fputs("        -? = help\n\n",stdout);
  435.     fputs("If you like FREE send me a picture postcard or $5. I can be reached at:\n\n",stdout);
  436.     fputs("VOICE: Howard Kapustein            MODEM: The BOSS (201) 568-7293 **Support BBS\n",stdout);
  437.     fputs("       1695 Barbara Lane                  Software Society (201) 729-7410\n",stdout);
  438.     fputs("       East Meadow, New York 11554        Computer Connection (202) 547-2008\n",stdout);
  439.     fputs("       Phone: (516) 481-9612              GEnie: Howard S. Kapustein\n\n",stdout);
  440.     fputs("I am a poor starving college senior,          404 Davison\n",stdout);
  441.     fputs("so all donations (and job offers)             Rensselaer Polytechnic Institute\n",stdout);
  442.     fputs("will be appreciated. Until graduation         Troy, NY 12180\n",stdout);
  443.     fputs("(May 19, 1989) PLEASE contact me at ->        Phone: (518) 276-7381\n\n",stdout);
  444.     fputs("I also write Subtree Find (the Fastest file finder, also supports paths),\n",stdout);
  445.     fputs("TCHK (a Turbo C library for Turbo C 2.0, over 200 functions) and more.",stdout);
  446.     bye(9);
  447. }
  448.  
  449.  
  450. void waitforkey(int keycode, boolean msg)   /* wait for keypress when done */
  451. {
  452.     int key;
  453.  
  454.     if (msg) {
  455.         if (keycode == 0)
  456.             fputs("Press any key to end...",stdout);
  457.         else
  458.             printf("Press key #%d to end...",keycode);
  459.     }
  460.     if (keycode == 0)
  461.         bioskey(0);
  462.     else
  463.         do {
  464.             key = bioskey(0);
  465.             if (key & 0xFF)
  466.                 key = key & 0xFF;
  467.             else
  468.                 key = ((key & 0xFF00) >> 8) + 256;
  469.         } while (key != keycode);
  470. }
  471.  
  472.  
  473. void parsecommandline(char *cmdline)
  474. {
  475.     extern struct options flag;
  476.     int i;
  477.  
  478.     for (i=0; cmdline[i]!='\0'; i++)
  479.         switch (toupper(cmdline[i])) {
  480.             case '/': { if (cmdline[++i] == '\0')
  481.                             bye(67);
  482.                         break; }
  483.             case 'N': { switch (cmdline[i+1]) {
  484.                             case '-': { flag.network = FALSE;
  485.                                         i++;
  486.                                         break; }
  487.                             case '*': { flag.network ^= TRUE;
  488.                                         i++;
  489.                                         break; }
  490.                             case '+': i++;
  491.                              default: flag.network = TRUE;  break;
  492.                         }
  493.                         break; }
  494.             case 'E': { switch (cmdline[i+1]) {
  495.                             case '-': { flag.errormsg = FALSE;
  496.                                         i++;
  497.                                         break; }
  498.                             case '*': { flag.errormsg ^= TRUE;
  499.                                         i++;
  500.                                         break; }
  501.                             case '+': i++;
  502.                              default: flag.errormsg = TRUE;  break;
  503.                         }
  504.                         break; }
  505.             case 'F': { switch (cmdline[i+1]) {
  506.                             case '-': { flag.scandrives &= ~FLOPPY;
  507.                                         i++;
  508.                                         break; }
  509.                             case '*': { flag.scandrives ^= FLOPPY;
  510.                                         i++;
  511.                                         break; }
  512.                             case '+': i++;
  513.                              default: flag.scandrives |= FLOPPY;  break;
  514.                         }
  515.                         break; }
  516.             case 'H': { switch (cmdline[i+1]) {
  517.                             case '-': { flag.scandrives &= ~FIXED;
  518.                                         i++;
  519.                                         break; }
  520.                             case '*': { flag.scandrives ^= FIXED;
  521.                                         i++;
  522.                                         break; }
  523.                             case '+': i++;
  524.                              default: flag.scandrives |= FIXED;  break;
  525.                         }
  526.                         break; }
  527.             case 'X': { switch (cmdline[i+1]) {
  528.                             case '-': { flag.scandrives &= ~ALLDRIVES;
  529.                                         i++;
  530.                                         break; }
  531.                             case '*': { flag.scandrives ^= ALLDRIVES;
  532.                                         i++;
  533.                                         break; }
  534.                             case '+': i++;
  535.                              default: flag.scandrives |= ALLDRIVES;  break;
  536.                         }
  537.                         break; }
  538.             case 'W': { if (isdigit(cmdline[i+1])) {
  539.                             flag.waitmsg = (cmdline[++i] == '0') ? FALSE : TRUE;
  540.                             flag.waitkey = atoi(cmdline+i);
  541.                             for (; isdigit(cmdline[i+1]); i++);
  542.                         } else
  543.                             flag.waitkey = 0;
  544.                         switch (cmdline[i+1]) {
  545.                             case '-': { flag.wait = FALSE;
  546.                                         i++;
  547.                                         break; }
  548.                             case '*': { flag.wait ^= TRUE;
  549.                                         i++;
  550.                                         break; }
  551.                             case '+': i++;
  552.                              default: flag.wait = TRUE;  break;
  553.                         }
  554.                         break; }
  555.             case '?': help();
  556.             default : { printf("Unknown parameter: %c\n",cmdline[i]);  bye(11); }
  557.         }
  558. }
  559.  
  560.  
  561. void bye(int errcode)                       /* generalized quit routine */
  562. {
  563.     exit(errcode);
  564. }
  565.  
  566.  
  567. #if !defined(MATH_FP87) && !defined(MATH_EMU)   /* using no math library */
  568. struct rdiv_t rdiv(long int numer, long int denom, int precision)   /* real division without FP emulator library */
  569. {
  570.     struct rdiv_t retval;
  571.     long i;
  572.  
  573.     if (denom == 0l) {
  574.         retval.rint = -1;
  575.         retval.rfrac = -1;
  576.         return(retval);
  577.     }
  578.     retval.rint = numer / denom;
  579.     denom -= numer * retval.rint;
  580.     retval.rfrac = 0l;
  581.     if (denom == 0l)
  582.         return(retval);
  583.     for (; precision >= 0; precision--) {
  584.         numer *= 10l;
  585.         i = numer / denom;
  586.         if (precision != 0)
  587.             retval.rfrac = retval.rfrac * 10l  +  i;
  588.         else
  589.             if (numer / denom >= 5l)          /* round off last decimal place */
  590.                 retval.rfrac++;
  591.         numer = numer - i * denom;
  592.     }
  593.     return(retval);
  594. }
  595. #endif
  596.