home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / BDSC / BDSC-1 / STDLIB2.C < prev    next >
Text File  |  2000-06-30  |  17KB  |  652 lines

  1. /*
  2.     STDLIB2.C -- for BDS C v1.45 -- LZ, 11/9/81
  3.  
  4.     This file contains the source for the following
  5.     library functions:
  6.  
  7.     printf         fprintf        sprintf        _spr
  8.     scanf        fscanf        sscanf        _scn
  9.     fgets
  10.     puts        fputs
  11.     swapin
  12.  
  13.     Note that all the upper-level formatted I/O functions
  14.     ("printf", "fprintf", "scanf", and "fscanf") now use
  15.     "_spr" and "_scn" for doing conversions. While
  16.     this leads to very modularized source code, it also
  17.     means that calls to "scanf" and "fscanf" must process
  18.     ALL the information on a line of text; if the format
  19.     string runs out and there is still text left in the
  20.     line being processed, the text will be lost (i.e., the
  21.     NEXT scanf or fscanf call will NOT find it.)
  22.  
  23.     An alternate version of "_spr" is given in the file
  24.     FLOAT.C for use with floating point numbers; see FLOAT.C
  25.     for details. Since "_spr" is used by "printf", this
  26.     really amounts to an alternate version of "printf."
  27.  
  28.     Also note that temporary work space is declared within
  29.     each of the high-level functions as a one-dimensional
  30.     character array. The length limit on this array is
  31.     presently set to 132 by the #define MAXLINE statement;
  32.     if you intend to create longer lines through printf,
  33.     fprintf, scanf, or fscanf calls, be SURE to raise this
  34.     limit by changing the #define statement.
  35.  
  36.     Some misc. comments on hacking text files with CP/M:
  37.     The conventional CP/M text format calls for each
  38.     line to be terminated by a CR-LF combination. In the
  39.     world of C programming, though, we like to just use
  40.     a single LF (also called a newline) to terminate
  41.     lines. AND SO, the functions which deal with reading
  42.     and writing text lines from disk files to memory and
  43.     vice-versa ("fgets", "fputs") take special pains to
  44.     convert    CR-LF combinations into single '\n' characters
  45.     when reading from disk ("fgets"), and convert '\n'
  46.     characters to CR-LF combinations when writing TO disk
  47.     ("fputs"). This allows the C programmer to do things
  48.     in style, dealing only with a single line terminator
  49.     while the text is in memory, while maintaining compat-
  50.     ibility with the CP/M text format for disk files (so
  51.     that, for example, a text file can be "type"d under
  52.     the CCP.)
  53.  
  54.     Note that the "gets" function (which simply buffers up
  55.     a line of console input at a given buffer location)
  56.     terminates the line with a null byte ('\0') WITHOUT
  57.     any CR or LF.
  58.  
  59.     Remember to put out a CPMEOF (control-Z or 0x1a) byte
  60.     at the end of text files being written out to disk.
  61.  
  62.     Also, watch out when reading in text files using
  63.     "getc". While a text file is USUALLY terminated
  64.     with a control-Z, it MAY NOT BE if the file ends
  65.     on an even sector boundary (although respectable
  66.     editors will now usually make sure the control-Z
  67.     is always there.) This means that there are two
  68.     possible return values from "getc" which can signal
  69.     an End-of file: CPMEOF (0x1a) or ERROR (-1, or 255
  70.     if you assign it to a char variable) should the CPMEOF
  71.     be missing.
  72. */
  73.  
  74. #include "bdscio.h"
  75.  
  76. char toupper(), isdigit();
  77.  
  78. /*
  79.     printf
  80.  
  81.     usage:
  82.         printf(format, arg1, arg2, ...);
  83.     
  84.     Note that since the "_spr" function is used to
  85.     form the output string, and then "puts" is used to
  86.     actually print it out, care must be taken to 
  87.     avoid generating null (zero) bytes in the output,
  88.     since such a byte will terminate printing of the
  89.     string by puts. Thus, a statment such as:
  90.  
  91.         printf("%c foo",'\0');
  92.  
  93.     would print nothing at all.
  94.  
  95.     This is my latest version of the "printf" standard library
  96.     routine. This time, folks, it REALLY IS standard. I've
  97.     tried to make it EXACTLY the same as the version presented
  98.     in Kernighan & Ritchie: right-justification of fields is
  99.     now the default instead of left-justification (you can have
  100.     left-justification by using a dash in the conversion, as
  101.     specified in the book); the "%s" conversion can take a precision
  102.     now as well as a field width; the "e" and "f" conversions, for
  103.     floating point numbers, are supported in a special version of
  104.     "_spr" given in source form in the FLOAT.C file. If you do
  105.     a lot of number crunching and wish to have that version be the
  106.     default (it eats up a K or two more than this version), just
  107.     replace the version of "_spr" in DEFF.CRL with the one in FLOAT.C,
  108.     using the CLIB program, or else be stuck with always typing in
  109.     "float" on the clink command line...
  110. */
  111.  
  112. printf(format)
  113. char *format;
  114. {
  115.     char line[MAXLINE];
  116.     _spr(line,&format);    /* use "_spr" to form the output */
  117.     puts(line);        /* and print out the line     */
  118. }
  119.  
  120.  
  121. /*
  122.     scanf:
  123.     This one accepts a line of input text from the
  124.     console, and converts the text to the required
  125.     binary or alphanumeric form (see Kernighan &
  126.     Ritchie for a more thorough description):
  127.     Usage:
  128.         scanf(format, ptr1, ptr2, ...);
  129.  
  130.     Returns number of items matched.
  131.  
  132.     Since a new line of text must be entered from the
  133.     console each time scanf is called, any unprocessed
  134.     text left over from the last call is lost forever.
  135.     This is a difference between BDS scanf and UNIX
  136.     scanf. Another is that the field width specification
  137.     is not supported here.
  138. */
  139.  
  140. int scanf(format)
  141. char *format;
  142. {
  143.     char line[MAXLINE];
  144.     gets(line);            /* get a line of input from user */
  145.     return _scn(line,&format);    /* and scan it with "_scn"     */
  146. }
  147.  
  148.  
  149. /*
  150.     fprintf:
  151.     Like printf, except that the first argument is
  152.     a pointer to a buffered I/O buffer, and the text
  153.     is written to the file described by the buffer:
  154.     ERROR (-1) returned on error.
  155.  
  156.     usage:
  157.         fprintf(iobuf, format, arg1, arg2, ...);
  158. */
  159.  
  160. int fprintf(iobuf,format)
  161. char *format;
  162. struct _buf *iobuf;
  163. {
  164.     char text[MAXLINE];
  165.     _spr(text,&format);
  166.     return fputs(text,iobuf);
  167. }
  168.  
  169.  
  170. /*
  171.     fscanf:
  172.     Like scanf, except that the first argument is
  173.     a pointer to a buffered input file buffer, and
  174.     the text is taken from the file instead of from
  175.     the console.
  176.     Usage:
  177.         fscanf(iobuf, format, ptr1, ptr2, ...);
  178.     Returns number of items matched (zero on EOF.)
  179.     Note that any unprocessed text is lost forever. Each
  180.     time scanf is called, a new line of input is gotten
  181.     from the file, and any information left over from
  182.     the last call is wiped out. Thus, the text in the
  183.     file must be arranged such that a single call to
  184.     fscanf will always get all the required data on a
  185.     line. This is not compatible with the way UNIX does
  186.     things, but it eliminates the need for separate
  187.     scanning functions for files, strings, and console
  188.     input; it is more economical to let both "fscanf" and
  189.     "scanf" use "sscanf". If you want to be able to scan
  190.     a partial line with fscanf and have the rest still be
  191.     there on the next fscanf call, you'll have to rewrite
  192.     fscanf to be self contained (not use sscanf) and use
  193.     "ungetc" to push back characters.
  194.  
  195.     Returns number of items succesfully matched.
  196. */
  197.  
  198. int fscanf(iobuf,format)
  199. char *format;
  200. struct _buf *iobuf;
  201. {
  202.     char text[MAXLINE];
  203.     if (!fgets(text,iobuf)) return 0;
  204.     return _scn(text,&format);
  205. }
  206.  
  207.  
  208. /*
  209.     sprintf:
  210.     Like fprintf, except a string pointer is specified
  211.     instead of a buffer pointer. The text is written
  212.     directly into memory where the string pointer points.
  213.  
  214.     Usage:
  215.         sprintf(string,format,arg1, arg2, ...);
  216. */
  217.  
  218. sprintf(buffer,format)
  219. char *buffer, *format;
  220. {
  221.     _spr(buffer,&format);    /* call _spr to do all the work */
  222. }
  223.  
  224.  
  225. /*
  226.     sscanf:
  227.  
  228.     Reads a line of text in from the console and scans it
  229.     for variable values specified in the format string. Uses
  230.     "_scn" for actual conversions; see the comments below in
  231.     the _scn function for more details.
  232.  
  233.     Usage:
  234.         scanf(format,&arg1,&arg2,...);
  235. */
  236.  
  237. int sscanf(line,format)
  238. char *line, *format;
  239. {
  240.     return _scn(line,&format);    /* let _scn do all the work */
  241. }
  242.  
  243.  
  244.  
  245. /*
  246.     General formatted output conversion routine, used by
  247.     fprintf and sprintf..."line" is where the output is
  248.     written, and "fmt" is a pointer to an argument list 
  249.     which must consist of a format string pointer and
  250.     subsequent list of (optional) values. Having arguments
  251.     passed on the stack works out a heck of a lot neater
  252.     than it did before when the args were passed via an
  253.     absolute vector in low memory!
  254. */
  255.  
  256.  
  257. _spr(line,fmt)
  258. char *line, **fmt;
  259. {
  260.     char _uspr(), c, base, *sptr, *format;
  261.     char wbuf[MAXLINE], *wptr, pf, ljflag, zfflag;
  262.     int width, precision,  *args;
  263.  
  264.     format = *fmt++;    /* fmt first points to the format string    */
  265.     args = fmt;        /* now fmt points to the first arg value    */
  266.  
  267.     while (c = *format++)
  268.       if (c == '%') {
  269.         wptr = wbuf;
  270.         precision = 6;
  271.         ljflag = pf = zfflag = 0;
  272.  
  273.         if (*format == '-') {
  274.             format++;
  275.             ljflag++;
  276.          }
  277.  
  278.  
  279.         if (*format == '0') zfflag++;    /* zero-fill feature test */
  280.  
  281.         width = (isdigit(*format)) ? _gv2(&format) : 0;
  282.  
  283.         if ((c = *format++) == '.') {
  284.             precision = _gv2(&format);
  285.             pf++;
  286.             c = *format++;
  287.          }
  288.  
  289.         switch(toupper(c)) {
  290.  
  291.         case 'D':  if (*args < 0) {
  292.                 *wptr++ = '-';
  293.                 *args = -*args;
  294.                 width--;
  295.                 }
  296.  
  297.         case 'U':  base = 10; goto val;
  298.  
  299.         case 'X':  base = 16; goto val;
  300.  
  301.         case 'O':  base = 8;  /* note that arbitrary bases can be
  302.                          added easily before this line */
  303.  
  304.              val:  width -= _uspr(&wptr,*args++,base);
  305.                goto pad;
  306.  
  307.         case 'C':  *wptr++ = *args++;
  308.                width--;
  309.                goto pad;
  310.  
  311.         case 'S':  if (!pf) precision = 200;
  312.                sptr = *args++;
  313.                while (*sptr && precision) {
  314.                 *wptr++ = *sptr++;
  315.                 precision--;
  316.                 width--;
  317.                 }
  318.  
  319.              pad:  *wptr = '\0';
  320.              pad2: wptr = wbuf;
  321.                if (!ljflag)
  322.                 while (width-- > 0)
  323.                     *line++ = zfflag ? '0' : ' ';
  324.  
  325.                while (*line = *wptr++)
  326.                 line++;
  327.  
  328.                if (ljflag)
  329.                 while (width-- > 0)
  330.                     *line++ = ' ';
  331.                break;
  332.  
  333.          default:  *line++ = c;
  334.  
  335.          }
  336.       }
  337.       else *line++ = c;
  338.  
  339.     *line = '\0';
  340. }
  341.  
  342. /*
  343.     Internal routine used by "_spr" to perform ascii-
  344.     to-decimal conversion and update an associated pointer:
  345. */
  346.  
  347. int _gv2(sptr)
  348. char **sptr;
  349. {
  350.     int n;
  351.     n = 0;
  352.     while (isdigit(**sptr)) n = 10 * n + *(*sptr)++ - '0';
  353.     return n;
  354. }
  355.  
  356.  
  357. /*
  358.     Internal function which converts n into an ASCII
  359.     base `base' representation and places the text
  360.     at the location pointed to by the pointer pointed
  361.     to by `string'. Yes, you read that correctly.
  362. */
  363.  
  364. char _uspr(string, n, base)
  365. char **string;
  366. unsigned n;
  367. {
  368.     char length;
  369.     if (n<base) {
  370.         *(*string)++ = (n < 10) ? n + '0' : n + 55;
  371.         return 1;
  372.     }
  373.     length = _uspr(string, n/base, base);
  374.     _uspr(string, n%base, base);
  375.     return length + 1;
  376. }
  377.  
  378.  
  379. /*
  380.     General formatted input conversion routine. "line" points
  381.     to a string containing ascii text to be converted, and "fmt"
  382.     points to an argument list consisting of first a format
  383.     string and then a list of pointers to the destination objects.
  384.  
  385.     Appropriate data is picked up from the text string and stored
  386.     where the pointer arguments point according to the format string.
  387.     See K&R for more info. The field width specification is not
  388.     supported by this version.
  389.  
  390.     NOTE: the "%s" termination character has been changed
  391.     from "any white space" to the character following the "%s"
  392.     specification in the format string. That is, the call
  393.  
  394.         sscanf(string, "%s:", &str);
  395.  
  396.     would ignore leading white space (as is the case with all
  397.     format conversions), and then read in ALL subsequent text
  398.     (including newlines) into the buffer "str" until a COLON
  399.     or null byte is encountered.
  400.  
  401. */
  402.  
  403. int _scn(line,fmt)
  404. char *line, **fmt;
  405. {
  406.     char sf, c, base, n, *sptr, *format;
  407.     int sign, val, **args;
  408.  
  409.     format = *fmt++;    /* fmt first points to the format string */
  410.     args = fmt;        /* now it points to the arg list */
  411.  
  412.     n = 0;
  413.     while (c = *format++)
  414.     {
  415.        if (isspace(c)) continue;    /* skip white space in format string */
  416.        if (c != '%')        /* if not %, must match text */
  417.         {
  418.         if (c != _igs(&line)) return n;
  419.         else line++;
  420.         }
  421.        else        /* process conversion */
  422.         {
  423.         sign = 1;
  424.         base = 10;
  425.         sf = 0;
  426.         if ((c = *format++) == '*')
  427.          {
  428.             sf++;        /* if "*" given, supress assignment */
  429.             c = *format++;
  430.          }
  431.         switch (toupper(c))
  432.          {
  433.            case 'X': base = 16;
  434.                  goto doval;
  435.  
  436.            case 'O': base = 8;
  437.                  goto doval;
  438.  
  439.            case 'D': if (_igs(&line) == '-') {
  440.                 sign = -1;
  441.                 line++;
  442.                   }
  443.  
  444.        doval:  case 'U': val = 0;
  445.                  if (_bc(_igs(&line),base) == ERROR)
  446.                 return n;
  447.                  while ((c = _bc(*line++,base)) != 255)
  448.                 val = val * base + c;
  449.                  line--;
  450.                  break;
  451.  
  452.            case 'S': _igs(&line);
  453.                  sptr = *args;
  454.                  while (c = *line++)   {
  455.                 if (c == *format) {
  456.                     format++;
  457.                     break;
  458.                  }
  459.                 if (!sf) *sptr++ = c;
  460.                   }                
  461.                  if (!sf) {
  462.                 n++;
  463.                 *sptr = '\0';
  464.                 args++;
  465.                   }
  466.                  continue;
  467.  
  468.            case 'C': if (!sf) {
  469.                 poke(*args++, *line);
  470.                 n++;
  471.                  }
  472.                  line++;
  473.                  continue;
  474.  
  475.            default:  return n;
  476.          }
  477.         if (!sf)
  478.          {
  479.             **args++ = val * sign;
  480.             n++;
  481.          }
  482.         }
  483.        if (!*line) return n;    /* if end of input string, return */
  484.     }
  485.     return n;
  486. }
  487.  
  488. /*
  489.     Internal function to position the character
  490.     pointer argument to the next non white-space
  491.     character in the string:
  492. */
  493.  
  494. char _igs(sptr)
  495. char **sptr;
  496. {
  497.     char c;
  498.     while (isspace(c = **sptr)) ++*sptr;
  499.     return (c);
  500. }
  501.  
  502.  
  503. /*
  504.     Internal function to convert character c to value
  505.     in base b , or return ERROR if illegal character for that
  506.     base:
  507. */
  508.  
  509. int _bc(c,b)
  510. char c,b;
  511. {
  512.     if (isalpha(c = toupper(c))) c -= 55;
  513.          else  if (isdigit(c))  c -= 0x30;
  514.      else return ERROR;
  515.     if (c > b-1) return ERROR;
  516.         else return c;
  517. }
  518.  
  519.  
  520. /*
  521.     puts:
  522.     Write out the given string to the console.
  523.     A newline is NOT automatically appended:
  524. */
  525.  
  526. puts(s)
  527. char *s;
  528. {
  529.     while (*s) putchar(*s++);
  530. }
  531.  
  532.  
  533. /*
  534.     fgets:
  535.     This next function is like "gets", except that
  536.     a) the line is taken from a buffered input file instead
  537.     of from the console, and b) the newline is INCLUDED in
  538.     the string and followed by a null byte. 
  539.     
  540.     This one is a little tricky due to the CP/M convention
  541.     of having a carriage-return AND a linefeed character
  542.     at the end of every text line. In order to make text
  543.     easier to deal with from C programs, this function (fgets)
  544.     automatically strips off the CR from any CR-LF combinations
  545.     that come in from the file. Any CR characters not im-
  546.     mediately followed by LF are left intact. The LF
  547.     is included as part of the string, and is followed
  548.     by a null byte. (Note that LF equals "newline".)
  549.     There is no limit to how long a line
  550.     can be here; care should be taken to make sure the
  551.     string pointer passed to fgets points to an area
  552.     large enough to accept any possible line length
  553.     (a line must be terminated by a newline (LF, or '\n')
  554.     character before it is considered complete.)
  555.  
  556.     The value NULL (defined to be 0 here) is returned
  557.     on EOF, whether it be a physical EOF (attempting to
  558.     read past last sector of the file) OR a logical EOF
  559.     (encountered a control-Z.) The 1.3 version didn't
  560.     recognize logical EOFs, because I did't realize how
  561.     SIMPLE it was to implement a buffered I/O "ungetc"
  562.     function.
  563. */
  564.  
  565. char *fgets(s,iobuf)
  566. char *s;
  567. struct _buf *iobuf;
  568. {
  569.     int count, c;
  570.     char *cptr;
  571.     count = MAXLINE;
  572.     cptr = s;
  573.     if ( (c = getc(iobuf)) == CPMEOF || c == EOF) return NULL;
  574.  
  575.     do {
  576.         if ((*cptr++ = c) == '\n') {
  577.           if (cptr>s+1 && *(cptr-2) == '\r')
  578.             *(--cptr - 1) = '\n';
  579.           break;
  580.         }
  581.      } while (count-- && (c=getc(iobuf)) != EOF && c != CPMEOF);
  582.  
  583.     if (c == CPMEOF) ungetc(c,iobuf);    /* push back control-Z */
  584.     *cptr = '\0';
  585.     return s;
  586. }
  587.  
  588.  
  589.  
  590. /*
  591.     fputs:
  592.     This function writes a string out to a buffered
  593.     output file. The '\n' character is expanded into
  594.     a CR-LF combination, in keeping with the CP/M
  595.     convention.
  596.     If a null ('\0') byte is encountered before a
  597.     newline is encountered, then there will be NO
  598.     automatic termination character appended to the
  599.     line.
  600.     ERROR (-1) returned on error.
  601. */
  602.  
  603. fputs(s,iobuf)
  604. char *s;
  605. struct _buf *iobuf;
  606. {
  607.     char c;
  608.     while (c = *s++) {
  609.         if (c == '\n') putc('\r',iobuf);
  610.         if (putc(c,iobuf) == ERROR) return ERROR;
  611.     }
  612.     return OK;
  613. }
  614.  
  615.  
  616. /*
  617.     swapin:
  618.     This is the swapping routine, to be used by the root
  619.     segment to swap in code segments under an overlay scheme.
  620.     See the "Overlays" document for details    on overlay schemes.
  621.  
  622.     Returns ERROR (-1) on error, OK (0) if segment loaded in OK.
  623.  
  624.     This version does not check to make sure that the code
  625.     yanked in doesn't overlap into the extenal data area (in
  626.     the interests of keeping the function short.) But, if you'd
  627.     like swapin to check for such problems, note that memory 
  628.     locations ram+115h and ram+116h contain the 16-bit address
  629.     of the base of the external data area (low order byte first,
  630.     as usual.) By rewriting swapin to read in one sector at a time
  631.     and check the addresses, accidental overlap into the data area
  632.     can be avoided.
  633. */
  634.  
  635. swapin(name,addr)
  636. char *name;        /* the file to swap in */
  637. {
  638.     int fd;
  639.     if (( fd = open(name,0)) == ERROR) {
  640.         printf("Swapin: cannot open %s\n",name);
  641.         return ERROR;
  642.     }
  643.     if ((read(fd,addr,512)) < 0) {
  644.         printf("Swapin: read error on %s\n",name);
  645.         close(fd);
  646.         return ERROR;
  647.     }
  648.     close(fd);
  649.     return OK;
  650. }
  651.  
  652.