Appendix D:

Small C Library Listings

    ______________________________________________________________________
                                   CLIB.H
    ______________________________________________________________________
    /*
    ** CLIB.H -- Definitions for Small-C library functions.
    **
    ** Copyright 1983  L. E. Payne and J. E. Hendrix
    */
    
    /*
    ** Misc parameters
    */
    #define MAXFILES 20  /* maximum open files */
    #define DOSEOF   26  /* DOS end-of-file byte */
    #define ARCHIVE  32  /* file archive bit */
    
    /*
    ** DOS function calls
    */
    #define CREATE   60  /* make file */
    #define OPEN     61  /* open file */
    #define CLOSE    62  /* close file or device */
    #define READ     63  /* read from a file */
    #define WRITE    64  /* write to a file */
    #define DELETE   65  /* delete file */
    #define SEEK     66  /* seek within a file */
    #define CONTROL  68  /* control device */
    #define FORCE    70  /* force use of a handle */
    #define RETDOS   76  /* close files and return to DOS */
    #define FNDFIL   78  /* find first occurrence of a file */
    #define FNDNXT   79  /* find next occurrence of a file */
    #define RENAME   86  /* rename file */
    
    /*
    ** File status bits
    */
    #define OPNBIT   1  /* open condition */
    #define EOFBIT   2  /* end-of-file condition */
    #define ERRBIT   4  /* error condition */
    
    /*
    ** File positioning origins
    */
    #define FROM_BEG  0  /* from beginning of file */
    #define FROM_CUR  1  /* from current position */
    #define FROM_END  2  /* from end of file */
    
    /*
    ** Buffer usage codes
    ** NULL means the buffer is not used.
    */
    #define EMPTY     1  /* buffer is currently empty */
    #define IN        2  /* buffer is currently holding input data */
    #define OUT       3  /* buffer is currently holding output data */
    
    /*
    ** ASCII characters
    */
    #define ABORT    3
    #define RUB      8
    #define PAUSE   19
    #define WIPE    24
    #define DEL    127

    ______________________________________________________________________
                                   ABS.C
    ______________________________________________________________________
    /*
    ** abs -- returns absolute value of nbr
    */
    abs(nbr)  int nbr; {
      if(nbr < 0) return (-nbr);
      return (nbr);
      }
    
    ______________________________________________________________________
                                   ATOI.C
    ______________________________________________________________________
    /*
    ** atoi(s) - convert s to integer.
    */
    atoi(s) char *s; {
      int sign, n;
      while(isspace(*s)) ++s;
      sign = 1;
      switch(*s) {
        case '-': sign = -1;
        case '+': ++s;
        }
      n = 0;
      while(isdigit(*s)) n = 10 * n + *s++ - '0';
      return (sign * n);
      }
    
    ______________________________________________________________________
                                   ATOIB.C
    ______________________________________________________________________
    /*
    ** atoib(s,b) - Convert s to "unsigned" integer in base b.
    **              NOTE: This is a non-standard function.
    */
    atoib(s, b) char *s; int b; {
      int n, digit;
      n = 0;
      while(isspace(*s)) ++s;
      while((digit = (127 & *s++)) >= '0') {
        if(digit >= 'a')      digit -= 87;
        else if(digit >= 'A') digit -= 55;
        else                  digit -= '0';
        if(digit >= b) break;
        n = b * n + digit;
        }
      return (n);
      }
    
    ______________________________________________________________________
                                   AUXBUF.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int
      _bufsiz[MAXFILES],  /* size of buffer */
      _bufptr[MAXFILES];  /* aux buffer address */
    
    /*
    ** auxbuf -- allocate an auxiliary input buffer for fd
    **   fd = file descriptor of an open file
    ** size = size of buffer to be allocated
    ** Returns NULL on success, else ERR.
    ** Note: Ungetc() still works.
    **       A 2nd call allocates a new buffer replacing old one.
    **       If fd is a device, buffer is allocated but ignored.
    **       Buffer stays allocated when fd is closed or new one is allocated.
    **       May be used on a closed fd.
    */
    auxbuf(fd, size) int fd; char *size; {   /* fake unsigned */
      if(!size || avail(NO) < size) return (ERR);
      _bufptr[fd] = malloc(size);
      _bufsiz[fd] = size;
      _empty(fd, NO);
      return (NULL);
      }
    
    ______________________________________________________________________
                                   AVAIL.C
    ______________________________________________________________________
    extern char *_memptr;
    /*
    ** Return the number of bytes of available memory.
    ** In case of a stack overflow condition, if 'abort'
    ** is non-zero the program aborts with an 'S' clue,
    ** otherwise zero is returned.
    */
    avail(abort) int abort; {
      char x;
      if(&x < _memptr) {
        if(abort) exit(1);
        return (0);
        }
      return (&x - _memptr);
      }
    
    ______________________________________________________________________
                                   BSEEK.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _nextc[], _bufuse[];
    /*
    ** Position fd to the character in file indicated by "offset."
    ** "Offset" is the address of a long integer or array of two
    ** integers containing the offset, low word first.
    ** 
    **     BASE     OFFSET-RELATIVE-TO
    **       0      beginning of file
    **       1      current byte in file
    **       2      end of file (minus offset)
    **
    ** Returns NULL on success, else EOF.
    */
    bseek(fd, offset, base) int fd, offset[], base; {
      int hi, lo;
      if(!_mode(fd) || !_bufuse[fd]) return (EOF);
      if(_adjust(fd)) return (EOF);
      lo = offset[0];
      hi = offset[1];
      if(!_seek(base, fd, &hi, &lo)) return (EOF);
      _nextc[fd] = EOF;
      _clreof(fd);
      return (NULL);
      }
    
    ______________________________________________________________________
                                   BTELL.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _bufuse[];
    /*
    ** Retrieve offset to next character in fd.
    ** "Offset" must be the address of a long int or
    ** a 2-element int array. The offset is placed in
    ** offset in low, high order.
    */
    btell(fd, offset) int fd, offset[]; {
      if(!_mode(fd) || !_bufuse[fd]) return (EOF);
      if(_adjust(fd)) return (EOF);
      offset[0] = offset[1] = 0;
      _seek(FROM_CUR, fd, offset+1, offset);
      return (NULL);
      }
    
    ______________________________________________________________________
                                   CALL.ASM
    ______________________________________________________________________
    ;
    ; Small-C Run Time Library for MS/PC-DOS
    ;
            extrn   __main: near
            extrn    _exit: near
            extrn   __memptr: word
    
    data   segment public
            dw      1
    data   ends
    
    stack   segment stack
            dw      32 dup(?)
    stack   ends
    
    code    segment public
            assume  cs:code
    start:
            mov     ax,data         ; set data segment for program
            mov     ds,ax
            mov     ax,es:[2]       ; paragraphs of memory on system
            sub     ax,data         ; paragraphs beyond code segment
            cmp     ah,10h          ; more than 64K?
            jb      start_1         ; no
            mov     ax,1000h        ; only use 64K
    start_1:
            mov     cl,4
            shl     ax,cl           ; byte offset to end of data/free/stack
            cli                     ; disable interrupts
            mov     bx,ds
            mov     ss,bx           ; make data and stack segments coincide
            mov     sp,ax           ; top of stack = end of data/free/stack
            push    ax              ; force sp non-zero (if 64K used)
            sti                     ; reenable interrupts
            mov     ax,stack        ; paragraph following data
            sub     ax,data         ; number of data paragraphs
            shl     ax,cl           ; number of data bytes (offset to free/stack)
            mov     bx,ax
            inc     bh              ; adjust for minimum stack space
            cmp     bx,sp           ; enough memory?
            jb      start_2         ; yes
            mov     ax,1            ; no, terminate with exit code 1
            push    ax
            call    _exit
    start_2:
            mov     __memptr,ax     ; set memory allocation pointer
    ;
    ; ------------ release unused memory -----------
    ; ------ cannot run debug with this code -------
    ;        mov     bx,sp
    ;        mov     ah,4AH
    ;        int     21H
    ; ----------------------------------------------
    ;
    ; make sure that es -> psp, because __main requires it
    ;
            jmp     __main          ; __main never returns
    
            public  _ccargc
    _ccargc:
            mov     al,cl
            xor     ah,ah
            ret
    
    ;
    ; Test if Secondary (BX) <oper> Primary (AX)
    ;
    compare macro   name, cond
            public  __&name
    __&name:
            cmp     ax,bx
            j&cond  true
            xor     ax,ax   ; returns zero
            ret
            endm
    ;
            compare ult,a
            compare ugt,b
            compare ule,ae
            compare uge,be
            compare eq,e
            compare ne,ne
            compare lt,g
            compare gt,l
            compare le,ge
            compare ge,le
    
    ;
    ; Logical Negate of Primary
    ;
            public  __lneg
    __lneg:
            or      ax,ax
            jnz     false
    true:   mov     ax,1    ; returns one
            ret
    false:  xor     ax,ax   ; returns zero
            ret
    ;
    ;
    ; execute "switch" statement
    ;
    ;  ax  =  switch value
    ; (sp) -> switch table
    ;         dw addr1, value1
    ;         dw addr2, value2
    ;         ...
    ;         dw 0
    ;        [jmp default]
    ;         continuation
    ;
            public  __switch
    __switch:
            pop     bx              ; bx -> switch table
            jmp     short skip      ; skip the pre-increment
    loop:
            add     bx,4
    skip:   mov     cx,cs:[bx]
            jcxz    default         ; end of table -- jump out
            cmp     ax,cs:[bx+2]
            jnz     loop
            jmp     cx              ; match -- jump to case
    default:
            inc     bx
            inc     bx
            jmp     bx              ; jump to default/continuation
    ;
    ; dummy entry point to resolve the external reference _LINK
    ; which is no longer generated by Small-C but which exists in
    ; library modules and .OBJ files compiled by earlier versions
    ; of Small-C
            public  __link
    __link: ret
    
    code ends
            end     start
    
    ______________________________________________________________________
                                   CALLOC.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Cleared-memory allocation of n items of size bytes.
    ** n     = Number of items to allocate space for.
    ** size  = Size of the items in bytes.
    ** Returns the address of the allocated block,
    ** else NULL for failure.
    */
    calloc(n, size) unsigned n, size; {
      return (_alloc(n*size, YES));
      }
    
    ______________________________________________________________________
                                   CLEARERR.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _status[];
    /*
    ** Clear error status for fd.
    */
    clearerr(fd) int fd; {
      if(_mode(fd)) _clrerr(fd);
      }
    
    ______________________________________________________________________
                                   CSEEK.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _nextc[], _bufuse[];
    /*
    ** Position fd to the 128-byte record indicated by
    ** "offset" relative to the point indicated by "base."
    ** 
    **     BASE     OFFSET-RELATIVE-TO
    **       0      first record
    **       1      current record
    **       2      end of file (last record + 1)
    **              (offset should be minus)
    **
    ** Returns NULL on success, else EOF.
    */
    cseek(fd, offset, base) int fd, offset, base; {
      int newrec, oldrec, hi, lo;
      if(!_mode(fd) || !_bufuse[fd]) return (EOF);
      if(_adjust(fd)) return (EOF);
      switch (base) {
         case 0: newrec = offset;
                 break;
         case 1: oldrec = ctell(fd);
                 goto calc;
         case 2: hi = lo = 0;
                 if(!_seek(FROM_END, fd, &hi, &lo)) return (EOF);
                 oldrec = ((lo >> 7) & 511) | (hi << 9);
         calc:
                 newrec = oldrec + offset;
                 break;
        default: return (EOF);
        }
      lo = (newrec << 7);       /* convert newrec to long int */
      hi = (newrec >> 9) & 127;
      if(!_seek(FROM_BEG, fd, &hi, &lo)) return (EOF);
      _nextc[fd] = EOF;
      _clreof(fd);
      return (NULL);
      }
    
    /*
    ** Position fd to the character indicated by
    ** "offset" within current 128-byte record.
    ** Must be on record boundary.
    **
    ** Returns NULL on success, else EOF.
    */
    cseekc(fd, offset) int fd, offset; {
      int hi, lo;
      if(!_mode(fd) || isatty(fd) ||
         ctellc(fd) || offset < 0 || offset > 127) return (EOF);
      hi = 0; lo = offset;
      if(!_seek(FROM_CUR, fd, &hi, &lo)) return (EOF);
      return (NULL);
      }
    
    ______________________________________________________________________
                                   CSYSLIB.C
    ______________________________________________________________________
    /*
    ** CSYSLIB -- System-Level Library Functions
    */
    
    #include "stdio.h"
    #include "clib.h"
    
    /*
    ****************** System Variables ********************
    */
    
    int
      _cnt=1,             /* arg count for main */
      _vec[20],           /* arg vectors for main */
      _status[MAXFILES] = {OPNBIT, OPNBIT, OPNBIT, OPNBIT, OPNBIT},
      _cons  [MAXFILES],  /* fast way to tell if file is the console */
      _nextc [MAXFILES] = {EOF, EOF, EOF, EOF, EOF},
      _bufuse[MAXFILES],  /* current buffer usage: NULL, EMPTY, IN, OUT */
      _bufsiz[MAXFILES],  /* size of buffer */
      _bufptr[MAXFILES],  /* aux buffer address */
      _bufnxt[MAXFILES],  /* address of next byte in buffer */
      _bufend[MAXFILES],  /* address of end-of-data in buffer */
      _bufeof[MAXFILES];  /* true if current buffer ends file */
    
    char
     *_memptr,           /* pointer to free memory. */
      _arg1[]="*";       /* first arg for main */
    
    /*
    *************** System-Level Functions *****************
    */
    
    /*
    **  Process command line, allocate default buffer to each fd,
    **  execute main(), and exit to DOS. Must be executed with es=psp.
    **  Small default buffers are allocated because a high price is paid for
    **  byte-by-byte calls to DOS. Tests gave these results for a simple
    **  copy program:
    **
    **          chunk size       copy time in seconds
    **              1                    36
    **              5                    12
    **             25                     6
    **             50                     6
    */
    _main() {
      int fd;
      _parse();
      for(fd = 0; fd < MAXFILES; ++fd) auxbuf(fd, 32);
      if(!isatty(stdin))  _bufuse[stdin]  = EMPTY;
      if(!isatty(stdout)) _bufuse[stdout] = EMPTY;
      main(_cnt, _vec);
      exit(0);
      }
    
    /*
    ** Parse command line and setup argc and argv.
    ** Must be executed with es == psp
    */
    _parse() {
      char *ptr;
    #asm
      mov     cl,es:[80h]  ; get parameter string length
      mov     ch,0       
      push    cx           ; save it
      inc     cx
      push    cx           ; 1st __alloc() arg
      mov     ax,1
      push    ax           ; 2nd __alloc() arg
      call    __alloc      ; allocate zeroed memory for args
      add     sp,4
      mov     [bp-2],ax    ; ptr = addr of allocated memory
      pop     cx
      push    es           ; exchange
      push    ds           ; es         (source)
      pop     es           ;    and
      pop     ds           ;        ds  (destination)
      mov     si,81h       ; source offset
      mov     di,[bp-2]    ; destination offset
      rep     movsb        ; move string
      mov     al,0
      stosb                ; terminate with null byte
      push    es
      pop     ds           ; restore ds
    #endasm
      _vec[0]=_arg1;       /* first arg = "*" */
      while (*ptr) {
        if(isspace(*ptr)) {++ptr; continue;}
        if(_cnt < 20) _vec[_cnt++] = ptr;
        while(*ptr) {
          if(isspace(*ptr)) {*ptr = NULL; ++ptr; break;}
          ++ptr;
          }
        }
      }
    
    /*
    ** Open file on specified fd.
    */
    _open(fn, mode, fd) char *fn, *mode; int *fd; {
      int rw, tfd;
      switch(mode[0]) {
        case 'r': {
          if(mode[1] == '+') rw = 2; else rw = 0;
          if ((tfd = _bdos2((OPEN<<8)|rw, NULL, NULL, fn)) < 0) return (NO);
          break;
          }
        case 'w': {
          if(mode[1] == '+') rw = 2; else rw = 1;
        create:
          if((tfd = _bdos2((CREATE<<8), NULL, ARCHIVE, fn)) < 0) return (NO);
          _bdos2(CLOSE<<8, tfd, NULL, NULL);
          if((tfd = _bdos2((OPEN<<8)|rw, NULL, NULL, fn)) < 0) return (NO);
          break;
          }
        case 'a': {
          if(mode[1] == '+') rw = 2; else rw = 1;
          if((tfd = _bdos2((OPEN<<8)|rw, NULL, NULL, fn)) < 0) goto create;
          if(_bdos2((SEEK<<8)|FROM_END, tfd, NULL, 0) < 0) return (NO);
          break;
          }
        default: return (NO);
        }
      _empty(tfd, YES);
      if(isatty(tfd)) _bufuse[tfd] = NULL;
      *fd = tfd;
      _cons  [tfd] = NULL;
      _nextc [tfd] = EOF;
      _status[tfd] = OPNBIT;
      return (YES);
      }
    
    /*
    ** Binary-stream input of one byte from fd.
    */
    _read(fd) int fd; {
      unsigned char ch;
      if(_nextc[fd] != EOF) {
        ch = _nextc[fd];
        _nextc[fd] = EOF;
        return (ch);
        }
      if(iscons(fd))  return (_getkey());
      if(_bufuse[fd]) return(_readbuf(fd));
      switch(_bdos2(READ<<8, fd, 1, &ch)) {
         case 1:  return (ch);
         case 0: _seteof(fd); return (EOF);
        default: _seterr(fd); return (EOF);
        }
      }
    
    /*
    ** Fill buffer if necessary, and return next byte.
    */
    _readbuf(fd) int fd; {
      int got, chunk;
      char *ptr, *max;
      if(_bufuse[fd] == OUT && _flush(fd)) return (EOF);
      while(YES) {
        ptr = _bufnxt[fd];
        if(ptr < _bufend[fd]) {++_bufnxt[fd]; return (*ptr);}
        if(_bufeof[fd]) {_seteof(fd); return (EOF);}
        max = (ptr = _bufend[fd] = _bufptr[fd]) + _bufsiz[fd];
        do {         /* avoid DMA problem on physical 64K boundary */
          if((max - ptr) < 512) chunk = max - ptr;
          else                  chunk = 512;
          ptr += (got = _bdos2(READ<<8, fd, chunk, ptr));
          if(got < chunk) {_bufeof[fd] = YES; break;}
          } while(ptr < max);
        _bufend[fd] = ptr;
        _bufnxt[fd] = _bufptr[fd];
        _bufuse[fd] = IN;
        }
      }
    
    /*
    ** Binary-Stream output of one byte to fd.
    */
    _write(ch, fd) int ch, fd; {
      if(_bufuse[fd]) return(_writebuf(ch, fd));
      if(_bdos2(WRITE<<8, fd, 1, &ch) != 1) {
        _seterr(fd);
        return (EOF);
        }
      return (ch);
      }
    
    /*
    ** Empty buffer if necessary, and store ch in buffer.
    */
    _writebuf(ch, fd) int ch, fd; {
      char *ptr;
      if(_bufuse[fd] == IN && _backup(fd)) return (EOF);
      while(YES) {
        ptr = _bufnxt[fd];
        if(ptr < (_bufptr[fd] + _bufsiz[fd])) {
          *ptr = ch;
          ++_bufnxt[fd];
          _bufuse[fd] = OUT;
          return (ch);
          }
        if(_flush(fd)) return (EOF);
        }
      }
    
    /*
    ** Flush buffer to DOS if dirty buffer.
    ** Reset buffer pointers in any case.
    */
    _flush(fd) int fd; {
      int i, j, k, chunk;
      if(_bufuse[fd] == OUT) {
        i = _bufnxt[fd] - _bufptr[fd];
        k = 0;
        while(i > 0) {     /* avoid DMA problem on physical 64K boundary */
          if(i < 512) chunk = i;
          else        chunk = 512;
          k += (j = _bdos2(WRITE<<8, fd, chunk, _bufptr[fd] + k));
          if(j < chunk) {_seterr(fd); return (EOF);}
          i -= j;
          }
        }
      _empty(fd, YES);
      return (NULL);
      }
    
    /*
    ** Adjust DOS file position to current point.
    */
    _adjust(fd) int fd; {
      if(_bufuse[fd] == OUT) return (_flush(fd));
      if(_bufuse[fd] == IN ) return (_backup(fd));
      }
    
    /*
    ** Backup DOS file position to current point.
    */
    _backup(fd) int fd; {
      int hi, lo;
      if(lo = _bufnxt[fd] - _bufend[fd]) {
        hi = -1;
        if(!_seek(FROM_CUR, fd, &hi, &lo)) {
          _seterr(fd);
          return (EOF);
          }
        }
      _empty(fd, YES);
      return (NULL);
      }
    
    /*
    ** Set buffer controls to empty status.
    */
    _empty(fd, mt) int fd, mt; {
      _bufnxt[fd] = _bufend[fd] = _bufptr[fd];
      _bufeof[fd] = NO;
      if(mt) _bufuse[fd] = EMPTY;
      }
    
    /*
    ** Return fd's open mode, else NULL.
    */
    _mode(fd) char *fd; {
      if(fd < MAXFILES) return (_status[fd]);
      return (NULL);
      }
    
    /*
    ** Set eof status for fd and
    */
    _seteof(fd) int fd; {
      _status[fd] |= EOFBIT;
      }
    
    /*
    ** Clear eof status for fd.
    */
    _clreof(fd) int fd; {
      _status[fd] &=  EOFBIT;
      }
    
    /*
    ** Set error status for fd.
    */
    _seterr(fd) int fd; {
      _status[fd] |= ERRBIT;
      }
    
    /*
    ** Clear error status for fd.
    */
    _clrerr(fd) int fd; {
      _status[fd] &=  ERRBIT;
      }
    
    /*
    ** Allocate n bytes of (possibly zeroed) memory.
    ** Entry: n = Size of the items in bytes.
    **    clear = "true" if clearing is desired.
    ** Returns the address of the allocated block of memory
    ** or NULL if the requested amount of space is not available.
    */
    _alloc(n, clear) unsigned n, clear; {
      char *oldptr;
      if(n < avail(YES)) {
        if(clear) pad(_memptr, NULL, n);
        oldptr = _memptr;
        _memptr += n;
        return (oldptr);
        }
      return (NULL);
      }
    
    /*
    ** Issue extended BDOS function and return result. 
    ** Entry: ax = function code and sub-function
    **        bx, cx, dx = other parameters
    */
    _bdos2(ax, bx, cx, dx) int ax, bx, cx, dx; {
    #asm
      push bx         ; preserve secondary register
      mov  dx,[bp+4]
      mov  cx,[bp+6]
      mov  bx,[bp+8]
      mov  ax,[bp+10] ; load DOS function number
      int  21h        ; call bdos
      jnc  __bdos21   ; no error
      neg  ax         ; make error code negative
    __bdos21:
      pop  bx         ; restore secondary register
    #endasm
      }
    
    /*
    ** Issue LSEEK call
    */
    _seek(org, fd, hi, lo) int org, fd, hi, lo; {
    #asm
      push bx         ; preserve secondary register
      mov  bx,[bp+4]
      mov  dx,[bx]    ; get lo part of destination
      mov  bx,[bp+6]
      mov  cx,[bx]    ; get hi part of destination
      mov  bx,[bp+8]  ; get file descriptor
      mov  al,[bp+10] ; get origin code for seek
      mov  ah,42h     ; move-file-pointer function
      int  21h        ; call bdos
      jnc  __seek1    ; error?
      xor  ax,ax      ; yes, return false
      jmp  __seek2 
    __seek1:          ; no, set hi and lo
      mov  bx,[bp+4]  ; get address of lo
      mov  [bx],ax    ; store low part of new position
      mov  bx,[bp+6]  ; get address of hi
      mov  [bx],dx    ; store high part of new position
      mov  ax,1       ; return true
    __seek2:
      pop  bx         ; restore secondary register
    #endasm
      }
    
    /*
    ** Test for keyboard input
    */
    _hitkey() {
    #asm
      mov  ah,1       ; sub-service = test keyboard
      int  16h        ; call bdos keyboard services
      jnz  __hit1
      xor ax,ax       ; nothing there, return false
      jmp  __hit2
    __hit1:
      mov  ax,1       ; character ready, return true
    __hit2:
    #endasm
      }
    
    /*
    ** Return next keyboard character
    */
    _getkey() {
    #asm
      mov  ah,0       ; sub-service = read keyboard
      int  16h        ; call bdos keyboard services
      or   al,al      ; special character?
      jnz  __get2     ; no
      mov  al,ah      ; yes, move it to al
      cmp  al,3       ; ctl-2 (simulated null)?
      jne  __get1     ; no
      xor  al,al      ; yes, report zero
      jmp  __get2
    __get1:
      add  al,113     ; offset to range 128-245
    __get2:
      xor  ah,ah      ; zero ah
    #endasm
      }
    
    ______________________________________________________________________
                                   CTELL.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _bufuse[];
    /*
    ** Return offset to current 128-byte record.
    */
    ctell(fd) int fd; {
      int hi, lo;
      if(!_mode(fd) || !_bufuse[fd]) return (-1);
      if(_adjust(fd)) return (-1);
      hi = lo = 0;
      _seek(FROM_CUR, fd, &hi, &lo);
      return ((hi << 9) | ((lo >> 7) & 511));
      }
    
    /*
    ** Return offset to next byte in current 128-byte record.
    */
    ctellc(fd) int fd; {
      int hi, lo;
      if(!_mode(fd) || !_bufuse[fd]) return (-1);
      if(_adjust(fd)) return (-1);
      hi = lo = 0;
      _seek(FROM_CUR, fd, &hi, &lo);
      return (lo & 127);
      }
    
    ______________________________________________________________________
                                   DTOI.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** dtoi -- convert signed decimal string to integer nbr
    **         returns field length, else ERR on error
    */
    dtoi(decstr, nbr)  char *decstr;  int *nbr;  {
      int len, s;
      if((*decstr)=='-') {s=1; ++decstr;} else s=0;
      if((len=utoi(decstr, nbr))<0) return ERR;
      if(*nbr<0) return ERR;
      if(s) {*nbr = -*nbr; return ++len;} else return len;
      }
    
    ______________________________________________________________________
                                   EXIT.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Close all open files and exit to DOS. 
    ** Entry: ec = exit code.
    */
    exit(ec) int ec; {
      int fd;  char str[4];
      ec &= 255;
      if(ec) {
        left(itou(ec, str, 4));
        fputs("Exit Code: ", stderr);
        fputs(str, stderr);
        fputs("\n", stderr);
        }
      for(fd = 0; fd < MAXFILES; ++fd) fclose(fd);
      _bdos2((RETDOS<<8)|ec, NULL, NULL, NULL);
    #asm
    _abort: jmp    _exit
            public _abort
    #endasm
      }
    
    ______________________________________________________________________
                                   FCLOSE.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Close fd 
    ** Entry: fd = file descriptor for file to be closed.
    ** Returns NULL for success, otherwise ERR
    */
    extern int _status[];
    fclose(fd) int fd; {
      if(!_mode(fd) || _flush(fd)) return (ERR);
      if(_bdos2(CLOSE<<8, fd, NULL, NULL) == -6) return (ERR);
      return (_status[fd] = NULL);
      }
    
    ______________________________________________________________________
                                   FEOF.C
    ______________________________________________________________________
    #include "clib.h"
    extern int _status[];
    /*
    ** Test for end-of-file status.
    ** Entry: fd = file descriptor
    ** Returns non-zero if fd is at eof, else zero.
    */
    feof(fd) int fd; {
      return (_status[fd] & EOFBIT);
      }
    
    ______________________________________________________________________
                                   FERROR.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern _status[];
    /*
    ** Test for error status on fd.
    */
    ferror(fd) int fd; {
      return (_status[fd] & ERRBIT);
      }
    
    ______________________________________________________________________
                                   FGETC.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    
    extern int _nextc[];
    
    /*
    ** Character-stream input of one character from fd.
    ** Entry: fd = File descriptor of pertinent file.
    ** Returns the next character on success, else EOF.
    */
    fgetc(fd) int fd; {
      int ch;                   /* must be int so EOF will flow through */
      if(_nextc[fd] != EOF) {   /* an ungotten byte pending? */
        ch = _nextc[fd];
        _nextc[fd] = EOF;
        return (ch & 255);      /* was cooked the first time */
        }
      while(1) {
        ch = _read(fd);
        if(iscons(fd)) {
          switch(ch) {          /* extra console cooking */
            case ABORT:  exit(2);
            case    CR: _write(CR, stderr); _write(LF, stderr); break;
            case   DEL:  ch = RUB;
            case   RUB:
            case  WIPE:  break;
            default:    _write(ch, stderr);
            }
          }
        switch(ch) {            /* normal cooking */
              default:  return (ch);
          case DOSEOF: _seteof(fd); return (EOF);
          case     CR:  return ('\n');
          case     LF:
          }
        }
      }
    #asm
    _getc:  jmp     _fgetc
            public  _getc
    #endasm
    
    ______________________________________________________________________
                                   FGETS.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Gets an entire string (including its newline
    ** terminator) or size-1 characters, whichever comes
    ** first. The input is terminated by a null character.
    ** Entry: str  = Pointer to destination buffer.
    **        size = Size of the destination buffer.
    **        fd   = File descriptor of pertinent file.
    ** Returns str on success, else NULL.
    */
    fgets(str, size, fd) char *str; unsigned size, fd; {
      return (_gets(str, size, fd, 1));
      }
    
    /*
    ** Gets an entire string from stdin (excluding its newline
    ** terminator) or size-1 characters, whichever comes
    ** first. The input is terminated by a null character.
    ** The user buffer must be large enough to hold the data.
    ** Entry: str  = Pointer to destination buffer.
    ** Returns str on success, else NULL.
    */
    gets(str) char *str; {
      return (_gets(str, 32767, stdin, 0));
      }
    
    _gets(str, size, fd, nl) char *str; unsigned size, fd, nl; {
      int backup; char *next;
      next = str;
      while(--size > 0) {
        switch (*next = fgetc(fd)) {
          case  EOF: *next = NULL;
                     if(next == str) return (NULL);
                     return (str);
          case '\n': *(next + nl) = NULL;
                     return (str);
          case  RUB: if(next > str) backup = 1; else backup = 0;
                     goto backout;
          case WIPE: backup = next - str;
            backout: if(iscons(fd)) {
                       ++size;
                       while(backup--) {
                         fputs("\b \b", stderr);
                         --next; ++size;
                         }
                       continue;
                       }
            default: ++next;
          }
        }
      *next = NULL;
      return (str);
      }
    
    ______________________________________________________________________
                                   FOPEN.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Open file indicated by fn.
    ** Entry: fn   = Null-terminated DOS file name.
    **        mode = "a"  - append
    **               "r"  - read
    **               "w"  - write
    **               "a+" - append update
    **               "r+" - read   update
    **               "w+" - write  update
    ** Returns a file descriptor on success, else NULL.
    */
    fopen(fn, mode) char *fn, *mode; {
      int fd;
      if(!_open(fn, mode, &fd)) return (NULL);
      return (fd);
      }
    
    ______________________________________________________________________
                                   FPRINTF.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** fprintf(fd, ctlstring, arg, arg, ...) - Formatted print.
    ** Operates as described by Kernighan & Ritchie.
    ** b, c, d, o, s, u, and x specifications are supported.
    ** Note: b (binary) is a non-standard extension.
    */
    fprintf(argc) int argc; {
      int *nxtarg;
      nxtarg = CCARGC() + &argc;
      return(_print(*(--nxtarg), --nxtarg));
      }
    
    /*
    ** printf(ctlstring, arg, arg, ...) - Formatted print.
    ** Operates as described by Kernighan & Ritchie.
    ** b, c, d, o, s, u, and x specifications are supported.
    ** Note: b (binary) is a non-standard extension.
    */
    printf(argc) int argc; {
      return(_print(stdout, CCARGC() + &argc - 1));
      }
    
    /*
    ** _print(fd, ctlstring, arg, arg, ...)
    ** Called by fprintf() and printf().
    */
    _print(fd, nxtarg) int fd, *nxtarg; {
      int  arg, left, pad, cc, len, maxchr, width;
      char *ctl, *sptr, str[17];
      cc = 0;                                         
      ctl = *nxtarg--;                          
      while(*ctl) {
        if(*ctl!='%') {fputc(*ctl++, fd); ++cc; continue;}
        else ++ctl;
        if(*ctl=='%') {fputc(*ctl++, fd); ++cc; continue;}
        if(*ctl=='-') {left = 1; ++ctl;} else left = 0;       
        if(*ctl=='0') pad = '0'; else pad = ' ';           
        if(isdigit(*ctl)) {
          width = atoi(ctl++);
          while(isdigit(*ctl)) ++ctl;
          }
        else width = 0;
        if(*ctl=='.') {
          maxchr = atoi(++ctl);
          while(isdigit(*ctl)) ++ctl;
          }
        else maxchr = 0;
        arg = *nxtarg--;
        sptr = str;
        switch(*ctl++) {
          case 'c': str[0] = arg; str[1] = NULL; break;
          case 's': sptr = arg;        break;
          case 'd': itoa(arg,str);     break;
          case 'b': itoab(arg,str,2);  break;
          case 'o': itoab(arg,str,8);  break;
          case 'u': itoab(arg,str,10); break;
          case 'x': itoab(arg,str,16); break;
          default:  return (cc);
          }
        len = strlen(sptr);
        if(maxchr && maxchr<len) len = maxchr;
        if(width>len) width = width - len; else width = 0; 
        if(!left) while(width--) {fputc(pad,fd); ++cc;}
        while(len--) {fputc(*sptr++,fd); ++cc; }
        if(left) while(width--) {fputc(pad,fd); ++cc;}  
        }
      return(cc);
      }
    
    ______________________________________________________________________
                                   FPUTC.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    extern int _status[];
    /*
    ** Character-stream output of a character to fd.
    ** Entry: ch = Character to write.
    **        fd = File descriptor of perinent file.
    ** Returns character written on success, else EOF.
    */
    fputc(ch, fd) int ch, fd; {
      switch(ch) {
        case  EOF: _write(DOSEOF, fd); break;
        case '\n': _write(CR, fd); _write(LF, fd); break;
          default: _write(ch, fd);
        }
      if(_status[fd] & ERRBIT) return (EOF);
      return (ch);
      }
    #asm
    _putc:  jmp     _fputc
            public  _putc
    #endasm
    
    ______________________________________________________________________
                                   FPUTS.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Write a string to fd. 
    ** Entry: string = Pointer to null-terminated string.
    **        fd     = File descriptor of pertinent file.
    */
    fputs(string, fd) char *string; int fd; {
      while(*string) fputc(*string++, fd) ;
      }
    
    ______________________________________________________________________
                                   FREAD.C
    ______________________________________________________________________
    #include "clib.h"
    extern int _status[];
    /*
    ** Item-stream read from fd.
    ** Entry: buf = address of target buffer
    **         sz = size of items in bytes
    **          n = number of items to read
    **         fd = file descriptor
    ** Returns a count of the items actually read.
    ** Use feof() and ferror() to determine file status.
    */
    fread(buf, sz, n, fd) unsigned char *buf; unsigned sz, n, fd; {
      return (read(fd, buf, n*sz)/sz);
      }
    
    /*
    ** Binary-stream read from fd.
    ** Entry:  fd = file descriptor
    **        buf = address of target buffer
    **          n = number of bytes to read
    ** Returns a count of the bytes actually read.
    ** Use feof() and ferror() to determine file status.
    */
    read(fd, buf, n) unsigned fd, n; unsigned char *buf; {
      unsigned cnt;
      cnt = 0;
      while(n--) {
        *buf++ = _read(fd);
        if(_status[fd] & (ERRBIT | EOFBIT)) break;
        ++cnt;
        }
      return (cnt);
      }
    
    ______________________________________________________________________
                                   FREE.C
    ______________________________________________________________________
    extern char *_memptr;
    /*
    ** free(ptr) - Free previously allocated memory block.
    ** Memory must be freed in the reverse order from which
    ** it was allocated.
    ** ptr    = Value returned by calloc() or malloc().
    ** Returns ptr if successful or NULL otherwise.
    */
    free(ptr) char *ptr; {
       return (_memptr = ptr);
       }
    #asm
    _cfree: jmp     _free
            public  _cfree
    #endasm
    
    ______________________________________________________________________
                                   FREOPEN.C
    ______________________________________________________________________
    #include <stdio.h>
    #include "clib.h"
    /*
    ** Close previously opened fd and reopen it. 
    ** Entry: fn   = Null-terminated DOS file name.
    **        mode = "a"  - append
    **               "r"  - read
    **               "w"  - write
    **               "a+" - append update
    **               "r+" - read   update
    **               "w+" - write  update
    **        fd   = File descriptor of pertinent file.
    ** Returns the original fd on success, else NULL.
    */
    extern int _status[];
    freopen(fn, mode, fd) char *fn, *mode; int fd; {
      int tfd;
      if(fclose(fd)) return (NULL);
      if(!_open(fn, mode, &tfd)) return (NULL);
      if(fd != tfd) {
        if(_bdos2(FORCE<<8, tfd, fd, NULL) < 0) return (NULL);
        _status[fd] = _status[tfd];
        _status[tfd] = 0;       /* leaves DOS using two handles */
        }
      return (fd);
      }
    
    ______________________________________________________________________
                                   FSCANF.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** fscanf(fd, ctlstring, arg, arg, ...) - Formatted read.
    ** Operates as described by Kernighan & Ritchie.
    ** b, c, d, o, s, u, and x specifications are supported.
    ** Note: b (binary) is a non-standard extension.
    */
    fscanf(argc) int argc; {
      int *nxtarg;
      nxtarg = CCARGC() + &argc;
      return (_scan(*(--nxtarg), --nxtarg));
      }
    
    /*
    ** scanf(ctlstring, arg, arg, ...) - Formatted read.
    ** Operates as described by Kernighan & Ritchie.
    ** b, c, d, o, s, u, and x specifications are supported.
    ** Note: b (binary) is a non-standard extension.
    */
    scanf(argc) int argc; {
      return (_scan(stdin, CCARGC() + &argc - 1));
      }
    
    /*
    ** _scan(fd, ctlstring, arg, arg, ...) - Formatted read.
    ** Called by fscanf() and scanf().
    */
    _scan(fd,nxtarg) int fd, *nxtarg; {
      char *carg, *ctl;
      unsigned u;
      int  *narg, wast, ac, width, ch, cnv, base, ovfl, sign;
      ac = 0;
      ctl = *nxtarg--;
      while(*ctl) {
        if(isspace(*ctl)) {++ctl; continue;}
        if(*ctl++ != '%') continue;
        if(*ctl == '*') {narg = carg = &wast; ++ctl;}
        else             narg = carg = *nxtarg--;
        ctl += utoi(ctl, &width);
        if(!width) width = 32767;
        if(!(cnv = *ctl++)) break;
        while(isspace(ch = fgetc(fd))) ;
        if(ch == EOF) {if(ac) break; else return(EOF);}
        ungetc(ch,fd);
        switch(cnv) {
          case 'c':
            *carg = fgetc(fd);
            break;
          case 's':
            while(width--) {
              if((*carg = fgetc(fd)) == EOF) break;
              if(isspace(*carg)) break;
              if(carg != &wast) ++carg;
              }
            *carg = 0;
            break;
          default:
            switch(cnv) {
              case 'b': base =  2; sign = 1; ovfl = 32767; break;
              case 'd': base = 10; sign = 0; ovfl =  3276; break;
              case 'o': base =  8; sign = 1; ovfl =  8191; break;
              case 'u': base = 10; sign = 1; ovfl =  6553; break;
              case 'x': base = 16; sign = 1; ovfl =  4095; break;
              default:  return (ac);
              }
            *narg = u = 0;
            while(width-- && !isspace(ch=fgetc(fd)) && ch!=EOF) {
              if(!sign)
                if(ch == '-') {sign = -1; continue;}
                else sign = 1;
              if(ch < '0') return (ac);
              if(ch >= 'a')      ch -= 87;
              else if(ch >= 'A') ch -= 55;
              else               ch -= '0';
              if(ch >= base || u > ovfl) return (ac);
              u = u * base + ch;
              }
            *narg = sign * u;
          }
        ++ac;                          
        }
      return (ac);
      }
    
    ______________________________________________________________________
                                   FWRITE.C
    ______________________________________________________________________
    #include "clib.h"
    extern int _status[];
    /*
    ** Item-stream write to fd.
    ** Entry: buf = address of source buffer
    **         sz = size of items in bytes
    **          n = number of items to write
    **         fd = file descriptor
    ** Returns a count of the items actually written or
    ** zero if an error occurred.
    ** May use ferror(), as always, to detect errors.
    */
    fwrite(buf, sz, n, fd) unsigned char *buf; unsigned sz, n, fd; {
      if(write(fd, buf, n*sz) == -1) return (0);
      return (n);
      }
    
    /*
    ** Binary-stream write to fd.
    ** Entry:  fd = file descriptor
    **        buf = address of source buffer
    **          n = number of bytes to write
    ** Returns a count of the bytes actually written or
    ** -1 if an error occurred.
    ** May use ferror(), as always, to detect errors.
    */
    write(fd, buf, n) unsigned fd, n; unsigned char *buf; {
      unsigned cnt;
      cnt = n;
      while(cnt--) {
        _write(*buf++, fd);
        if(_status[fd] & ERRBIT) return (-1);
        }
      return (n);
      }
    
    ______________________________________________________________________
                                   GETARG.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Get command line argument. 
    ** Entry: n    = Number of the argument.
    **        s    = Destination string pointer.
    **        size = Size of destination string.
    **        argc = Argument count from main().
    **        argv = Argument vector(s) from main().
    ** Returns number of characters moved on success,
    ** else EOF.
    */
    getarg(n, s, size, argc, argv)
      int n; char *s; int size, argc, argv[]; {
      char *str;
      int i;
      if(n < 0 | n >= argc) {
        *s = NULL;
        return EOF;
        }
      i = 0;
      str=argv[n];
      while(i<size) {
        if((s[i]=str[i])==NULL) break;
        ++i;
        }
      s[i]=NULL;
      return i;
      }
    
    ______________________________________________________________________
                                   GETCHAR.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Get next character from standard input. 
    */
    getchar() {
      return (fgetc(stdin));
      }
    
    ______________________________________________________________________
                                   IS.C
    ______________________________________________________________________
    /*
    ** All character classification functions except isascii().
    ** Integer argument (c) must be in ASCII range (0-127) for
    ** dependable answers.
    */
    
    #define ALNUM     1
    #define ALPHA     2
    #define CNTRL     4
    #define DIGIT     8
    #define GRAPH    16
    #define LOWER    32
    #define PRINT    64
    #define PUNCT   128
    #define BLANK   256
    #define UPPER   512
    #define XDIGIT 1024
    
    int _is[128] = {
     0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
     0x004, 0x104, 0x104, 0x104, 0x104, 0x104, 0x004, 0x004,
     0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
     0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
     0x140, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0,
     0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0,
     0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
     0x459, 0x459, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0,
     0x0D0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
     0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
     0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
     0x253, 0x253, 0x253, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x0D0,
     0x0D0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
     0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
     0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
     0x073, 0x073, 0x073, 0x0D0, 0x0D0, 0x0D0, 0x0D0, 0x004
     };
    
    isalnum (c) int c; {return (_is[c] & ALNUM );} /* 'a'-'z', 'A'-'Z', '0'-'9' */
    isalpha (c) int c; {return (_is[c] & ALPHA );} /* 'a'-'z', 'A'-'Z' */
    iscntrl (c) int c; {return (_is[c] & CNTRL );} /* 0-31, 127 */
    isdigit (c) int c; {return (_is[c] & DIGIT );} /* '0'-'9' */
    isgraph (c) int c; {return (_is[c] & GRAPH );} /* '!'-' ' */
    islower (c) int c; {return (_is[c] & LOWER );} /* 'a'-'z' */
    isprint (c) int c; {return (_is[c] & PRINT );} /* ' '-' ' */
    ispunct (c) int c; {return (_is[c] & PUNCT );} /* !alnum && !cntrl && !space */
    isspace (c) int c; {return (_is[c] & BLANK );} /* HT, LF, VT, FF, CR, ' ' */
    isupper (c) int c; {return (_is[c] & UPPER );} /* 'A'-'Z' */
    isxdigit(c) int c; {return (_is[c] & XDIGIT);} /* '0'-'9', 'a'-'f', 'A'-'F' */
    
    ______________________________________________________________________
                                   ISASCII.C
    ______________________________________________________________________
    /*
    ** return 'true' if c is an ASCII character (0-127)
    */
    isascii(c) unsigned c; {
      return (c < 128);
      }
    
    ______________________________________________________________________
                                   ISATTY.C
    ______________________________________________________________________
    /*
    ** Return "true" if fd is a device, else "false"
    */
    isatty(fd) int fd; {
    fd;               /* fetch handle */
    #asm
      push bx         ; save 2nd reg
      mov  bx,ax      ; place handle
      mov  ax,4400h   ; ioctl get info function
      int 21h         ; call BDOS
      pop  bx         ; restore 2nd reg
      mov  ax,dx      ; fetch info bits
      and  ax,80h     ; isdev bit
    #endasm
      }
    
    ______________________________________________________________________
                                   ISCONS.C
    ______________________________________________________________________
    /*
    ** Determine if fd is the console.
    */
    #include <stdio.h>
    extern int _cons[];
    
    iscons(fd) int fd; {
      if(_cons[fd] == NULL) {
        if(_iscons(fd)) _cons[fd] = 2;
        else            _cons[fd] = 1;
        }
      if(_cons[fd] == 1) return (NO);
      return (YES);
      }
    
    /*
    ** Call DOS only the first time for a file.
    */
    _iscons(fd) int fd; {
      fd;             /* fetch handle */
    #asm
      push bx         ; save 2nd reg
      mov  bx,ax      ; place handle
      mov  ax,4400h   ; ioctl get info function
      int 21h         ; call BDOS
      pop  bx         ; restore 2nd reg
      mov  ax,dx      ; fetch info bits
      and  ax,83h     ; keep device and console bits
      cmp  ax,80h     ; device and console?
      jg   __cons1
      xor  ax,ax      ; return false if not device and console
    __cons1:
    #endasm
      }
    
    ______________________________________________________________________
                                   ITOA.C
    ______________________________________________________________________
    /*
    ** itoa(n,s) - Convert n to characters in s 
    */
    itoa(n, s) char *s; int n; {
      int sign;
      char *ptr;
      ptr = s;
      if ((sign = n) < 0) n = -n;
      do {
        *ptr++ = n % 10 + '0';
        } while ((n = n / 10) > 0);
      if (sign < 0) *ptr++ = '-';
      *ptr = '\0';
      reverse(s);
      }
    
    ______________________________________________________________________
                                   ITOAB.C
    ______________________________________________________________________
    /*
    ** itoab(n,s,b) - Convert "unsigned" n to characters in s using base b.
    **                NOTE: This is a non-standard function.
    */
    itoab(n, s, b) int n; char *s; int b; {
      char *ptr;
      int lowbit;
      ptr = s;
      b >>= 1;
      do {
        lowbit = n & 1;
        n = (n >> 1) & 32767;
        *ptr = ((n % b) << 1) + lowbit;
        if(*ptr < 10) *ptr += '0'; else *ptr += 55;
        ++ptr;
        } while(n /= b);
      *ptr = 0;
      reverse (s);
      }
    
    ______________________________________________________________________
                                   ITOD.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** itod -- convert nbr to signed decimal string of width sz
    **         right adjusted, blank filled; returns str
    **
    **        if sz > 0 terminate with null byte
    **        if sz = 0 find end of string
    **        if sz < 0 use last byte for data
    */
    itod(nbr, str, sz)  int nbr;  char str[];  int sz;  {
      char sgn;
      if(nbr<0) {nbr = -nbr; sgn='-';}
      else sgn=' ';
      if(sz>0) str[--sz]=NULL;
      else if(sz<0) sz = -sz;
      else while(str[sz]!=NULL) ++sz;
      while(sz) {
        str[--sz]=(nbr%10+'0');
        if((nbr=nbr/10)==0) break;
        }
      if(sz) str[--sz]=sgn;
      while(sz>0) str[--sz]=' ';
      return str;
      }
    
    ______________________________________________________________________
                                   ITOO.C
    ______________________________________________________________________
    /*
    ** itoo -- converts nbr to octal string of length sz
    **         right adjusted and blank filled, returns str
    **
    **        if sz > 0 terminate with null byte
    **        if sz = 0 find end of string
    **        if sz < 0 use last byte for data
    */
    itoo(nbr, str, sz)  int nbr;  char str[];  int sz;  {
      int digit;
      if(sz>0) str[--sz]=0;
      else if(sz<0) sz = -sz;
      else while(str[sz]!=0) ++sz;
      while(sz) {
        digit=nbr&7; nbr=(nbr>>3)&8191;
        str[--sz]=digit+48;
        if(nbr==0) break;
        }
      while(sz) str[--sz]=' ';
      return str;
      }
    
    ______________________________________________________________________
                                   ITOU.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** itou -- convert nbr to unsigned decimal string of width sz
    **         right adjusted, blank filled; returns str
    **
    **        if sz > 0 terminate with null byte
    **        if sz = 0 find end of string
    **        if sz < 0 use last byte for data
    */
    itou(nbr, str, sz)  int nbr;  char str[];  int sz;  {
      int lowbit;
      if(sz>0) str[--sz]=NULL;
      else if(sz<0) sz = -sz;
      else while(str[sz]!=NULL) ++sz;
      while(sz) {
        lowbit=nbr&1;
        nbr=(nbr>>1)&32767;  /* divide by 2 */
        str[--sz]=((nbr%5)<<1)+lowbit+'0';
        if((nbr=nbr/5)==0) break;
        }
      while(sz) str[--sz]=' ';
      return str;
      }
    
    ______________________________________________________________________
                                   ITOX.C
    ______________________________________________________________________
    /*
    ** itox -- converts nbr to hex string of length sz
    **         right adjusted and blank filled, returns str
    **
    **        if sz > 0 terminate with null byte
    **        if sz = 0 find end of string
    **        if sz < 0 use last byte for data
    */
    itox(nbr, str, sz)  int nbr;  char str[];  int sz;  {
      int digit, offset;
      if(sz>0) str[--sz]=0;
      else if(sz<0) sz = -sz;
      else while(str[sz]!=0) ++sz;
      while(sz) {
        digit=nbr&15; nbr=(nbr>>4)&4095;
        if(digit<10) offset=48; else offset=55;
        str[--sz]=digit+offset;
        if(nbr==0) break;
        }
      while(sz) str[--sz]=' ';
      return str;
      }
    
    ______________________________________________________________________
                                   LEFT.C
    ______________________________________________________________________
    /*
    ** left -- left adjust and null terminate a string
    */
    left(str) char *str; {
      char *str2;
      str2=str;
      while(*str2==' ') ++str2;
      while(*str++ = *str2++);
      }

    ______________________________________________________________________
                                   LEXCMP.C
    ______________________________________________________________________
    
    char _lex[128] = {
           0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  /* NUL - /       */
          10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
          20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
          30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
          40, 41, 42, 43, 44, 45, 46, 47,
          65, 66, 67, 68, 69, 70, 71, 72, 73, 74,  /* 0-9           */
          48, 49, 50, 51, 52, 53, 54,              /* : ; < = > ? @ */
          75, 76, 77, 78, 79, 80, 81, 82, 83, 84,  /* A-Z           */
          85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
          95, 96, 97, 98, 99,100,
          55, 56, 57, 58, 59, 60,                  /* [ \ ] ^ _ `   */
          75, 76, 77, 78, 79, 80, 81, 82, 83, 84,  /* a-z           */
          85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
          95, 96, 97, 98, 99,100,
          61, 62, 63, 64,                          /* { | }         */
         127};                                     /* DEL           */
    
    /*
    ** lexcmp(s, t) - Return a number <0, 0, or >0
    **                as s is <, =, or > t.
    */
    lexcmp(s, t) char *s, *t; {
      while(lexorder(*s, *t) == 0)
        if(*s++) ++t;
        else return (0);
      return (lexorder(*s, *t));
      }
    
    /*
    ** lexorder(c1, c2)
    **
    ** Return a negative, zero, or positive number if
    ** c1 is less than, equal to, or greater than c2,
    ** based on a lexicographical (dictionary order)
    ** colating sequence.
    **
    */
    lexorder(c1, c2) int c1, c2; {
      return(_lex[c1] - _lex[c2]);
      }
    
    ______________________________________________________________________
                                   MALLOC.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Memory allocation of size bytes.
    ** size  = Size of the block in bytes.
    ** Returns the address of the allocated block,
    ** else NULL for failure.
    */
    malloc(size) unsigned size; {
      return (_alloc(size, NO));
      }
    
    ______________________________________________________________________
                                   OTOI.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** otoi -- convert unsigned octal string to integer nbr
    **          returns field size, else ERR on error
    */
    otoi(octstr, nbr)  char *octstr;  int *nbr;  {
      int d,t; d=0;
      *nbr=0;
      while((*octstr>='0')&(*octstr<='7')) {
        t=*nbr;
        t=(t<<3) + (*octstr++ - '0');
        if ((t>=0)&(*nbr<0)) return ERR;
        d++; *nbr=t;
        }
      return d;
      }
    
    ______________________________________________________________________
                                   PAD.C
    ______________________________________________________________________
    /*
    ** Place n occurrences of ch at dest.
    */
    pad(dest, ch, n) char *dest; unsigned n, ch; {
      while(n--) *dest++ = ch;
      }
    
    ______________________________________________________________________
                                   POLL.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Poll for console input or interruption
    */
    poll(pause) int pause; {
      int i;
      if(i = _hitkey())  i = _getkey();
      if(pause) {
        if(i == PAUSE) {
          i = _getkey();           /* wait for next character */
          if(i == ABORT) exit(2);  /* indicate abnormal exit */
          return (0);
          }
        if(i == ABORT) exit(2);
        }
      return (i);
      }
    
    ______________________________________________________________________
                                   PUTCHAR.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Write character to standard output. 
    */
    putchar(ch) int ch; {
      return (fputc(ch, stdout));
      }
    
    ______________________________________________________________________
                                   PUTS.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** Write string to standard output. 
    */
    puts(string) char *string; {
      fputs(string, stdout);
      fputc('\n', stdout);
      }
    
    ______________________________________________________________________
                                   RENAME.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Rename a file.
    **  from = address of old filename.
    **    to = address of new filename.
    **  Returns NULL on success, else ERR.
    */
    rename(from, to) char *from, *to; {
      if(_rename(from, to)) return (NULL);
      return (ERR);
      }
    
    _rename(old, new) char *old, *new; {
    #asm
      push ds         ; ds:dx points to old name
      pop  es         ; es:di points to new name
      mov  di,[bp+4]  ; get "new" offset
      mov  dx,[bp+6]  ; get "old" offset
      mov  ah,56h     ; rename function
      int  21h        ; call bdos
      jnc  __ren1     ; error?
      xor  ax,ax      ; yes, return false
      jmp  __ren2 
    __ren1:           ; no, set hi and lo
      mov  ax,1       ; return true
    __ren2:
    #endasm
      }
    
    ______________________________________________________________________
                                   REVERSE.C
    ______________________________________________________________________
    /*
    ** reverse string in place 
    */
    reverse(s) char *s; {
      char *j;
      int c;
      j = s + strlen(s) - 1;
      while(s < j) {
        c = *s;
        *s++ = *j;
        *j-- = c;
        }
      }
    
    
    ______________________________________________________________________
                                   REWIND.C
    ______________________________________________________________________
    /*
    ** Rewind file to beginning. 
    */
    rewind(fd) int fd; {
      return(cseek(fd, 0, 0));
      }
    
    ______________________________________________________________________
                                   SIGN.C
    ______________________________________________________________________
    /*
    ** sign -- return -1, 0, +1 depending on the sign of nbr
    */
    sign(nbr)  int nbr;  {
      if(nbr > 0)  return 1;
      if(nbr == 0) return 0;
      return -1;
      }
    
    ______________________________________________________________________
                                   STRCAT.C
    ______________________________________________________________________
    /*
    ** concatenate t to end of s 
    ** s must be large enough
    */
    strcat(s, t) char *s, *t; {
      char *d;
      d = s;
      --s;
      while (*++s) ;
      while (*s++ = *t++) ;
      return (d);
      }
    
    ______________________________________________________________________
                                   STRCHR.C
    ______________________________________________________________________
    /*
    ** return pointer to 1st occurrence of c in str, else 0
    */
    strchr(str, c) char *str, c; {
      while(*str) {
        if(*str == c) return (str);
        ++str;
        }
      return (0);
      }
    
    ______________________________________________________________________
                                   STRCMP.C
    ______________________________________________________________________
    /*
    ** return <0,   0,  >0 a_ording to
    **       s<t, s=t, s>t
    */
    strcmp(s, t) char *s, *t; {
      while(*s == *t) {
        if(*s == 0) return (0);
        ++s; ++t;
        }
      return (*s - *t);
      }
    
    ______________________________________________________________________
                                   STRCPY.C
    ______________________________________________________________________
    /*
    ** copy t to s 
    */
    strcpy(s, t) char *s, *t; {
      char *d;
      d = s;
      while (*s++ = *t++) ;
      return (d);
      }
    
    ______________________________________________________________________
                                   STRLEN.C
    ______________________________________________________________________
    /*
    ** return length of string s (fast version)
    */
    strlen(s) char *s; {
      #asm
      xor al,al        ; set search value to zero
      mov cx,65535     ; set huge maximum
      mov di,[bp+4]    ; get address of s
      cld              ; set direction flag forward
      repne scasb      ; scan for zero
      mov ax,65534
      sub ax,cx        ; calc and return length
      #endasm
      }
    
    ______________________________________________________________________
                                   STRNCAT.C
    ______________________________________________________________________
    /*
    ** concatenate n bytes max from t to end of s 
    ** s must be large enough
    */
    strncat(s, t, n) char *s, *t; int n; {
      char *d;
      d = s;
      --s;
      while(*++s) ;
      while(n--) {
        if(*s++ = *t++) continue;
        return(d);
        }
      *s = 0;
      return(d);
      }
    
    ______________________________________________________________________
                                   STRNCMP.C
    ______________________________________________________________________
    /*
    ** strncmp(s,t,n) - Compares two strings for at most n
    **                  characters and returns an integer
    **                  >0, =0, or <0 as s is >t, =t, or <t.
    */
    strncmp(s, t, n) char *s, *t; int n; {
      while(n && *s==*t) {
        if (*s == 0) return (0);
        ++s; ++t; --n;
        }
      if(n) return (*s - *t);
      return (0);
      }
    
    ______________________________________________________________________
                                   STRNCPY.C
    ______________________________________________________________________
    /*
    ** copy n characters from sour to dest (null padding)
    */
    strncpy(dest, sour, n) char *dest, *sour; int n; {
      char *d;
      d = dest;
      while(n-- > 0) {
        if(*d++ = *sour++) continue;
        while(n-- > 0) *d++ = 0;
        }
      *d = 0;
      return (dest);
      }
    
    ______________________________________________________________________
                                   STRRCHR.C
    ______________________________________________________________________
    /*
    ** strrchr(s,c) - Search s for rightmost occurrance of c.
    ** s      = Pointer to string to be searched.
    ** c      = Character to search for.
    ** Returns pointer to rightmost c or NULL.
    */
    strrchr(s, c) char *s, c; {
      char *ptr;
      ptr = 0;
      while(*s) {
        if(*s==c) ptr = s;
        ++s;
        }
      return (ptr);
      }
    
    ______________________________________________________________________
                                   TOASCII.C
    ______________________________________________________________________
    /*
    ** return ASCII equivalent of c
    */
    toascii(c) int c; {
      return (c);
      }
    
    ______________________________________________________________________
                                   TOLOWER.C
    ______________________________________________________________________
    /*
    ** return lower-case of c if upper-case, else c
    */
    tolower(c) int c; {
      if(c<='Z' && c>='A') return (c+32);
      return (c);
      }
    
    ______________________________________________________________________
                                   TOUPPER.C
    ______________________________________________________________________
    /*
    ** return upper-case of c if it is lower-case, else c
    */
    toupper(c) int c; {
      if(c<='z' && c>='a') return (c-32);
      return (c);
      }
    
    ______________________________________________________________________
                                   UNGETC.C
    ______________________________________________________________________
    #include "stdio.h"
    extern _nextc[];
    /*
    ** Put c back into file fd.
    ** Entry:  c = character to put back
    **        fd = file descriptor
    ** Returns c if successful, else EOF.
    */
    ungetc(c, fd) int c, fd; {
      if(!_mode(fd) || _nextc[fd]!=EOF || c==EOF) return (EOF);
      return (_nextc[fd] = c);
      }
    
    ______________________________________________________________________
                                   UNLINK.C
    ______________________________________________________________________
    #include "stdio.h"
    #include "clib.h"
    /*
    ** Unlink (delete) the named file. 
    ** Entry: fn = Null-terminated DOS file path\name.
    ** Returns NULL on success, else ERR.
    */
    unlink(fn) char *fn; {
      fn;           /* load fn into ax */
    #asm
      mov dx,ax     ; put fn in its place
      mov ah,41h    ; delete function code
      int 21h
      mov ax,0
      jnc __unlk    ; return NULL
      mov ax,-2     ; return ERR
    __unlk:
    #endasm
      }
    #asm
    _delete: jmp    _unlink
             public _delete
    #endasm
    
    ______________________________________________________________________
                                   UTOI.C
    ______________________________________________________________________
    #include "stdio.h"
    /*
    ** utoi -- convert unsigned decimal string to integer nbr
    **          returns field size, else ERR on error
    */
    utoi(decstr, nbr)  char *decstr;  int *nbr;  {
      int d,t; d=0;
      *nbr=0;
      while((*decstr>='0')&(*decstr<='9')) {
        t=*nbr;t=(10*t) + (*decstr++ - '0');
        if ((t>=0)&(*nbr<0)) return ERR;
        d++; *nbr=t;
        }
      return d;
      }
    
    ______________________________________________________________________
                                   XTOI.C
    ______________________________________________________________________
    #include stdio.h
    /*
    ** xtoi -- convert hex string to integer nbr
    **         returns field size, else ERR on error
    */
    xtoi(hexstr, nbr) char *hexstr; int *nbr; {
      int d, b;  char *cp;
      d = *nbr = 0; cp = hexstr;
      while(*cp == '0') ++cp;
      while(1) {
        switch(*cp) {
          case '0': case '1': case '2':
          case '3': case '4': case '5':
          case '6': case '7': case '8':
          case '9':                     b=48; break;
          case 'A': case 'B': case 'C':
          case 'D': case 'E': case 'F': b=55; break;
          case 'a': case 'b': case 'c':
          case 'd': case 'e': case 'f': b=87; break;
           default: return (cp - hexstr);
          }
        if(d < 4) ++d; else return (ERR);
        *nbr = (*nbr << 4) + (*cp++ - b);
        }
      }

Go to Appendix E Return to Table of Contents