home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vos2-121.zip / v / srcos2 / printf.c < prev    next >
C/C++ Source or Header  |  1998-11-05  |  16KB  |  288 lines

  1. /* ----------------------------------------------------------------- */
  2. /* PRINTF: diverts PRINTF calls to an OS/2 Named Queue               */
  3. /* Copyright (c) IBM Corporation, 1991, 1992                         */
  4. /* ----------------------------------------------------------------- */
  5. /* This version for OS/2 2.x, 32-bit programs.        Mike Cowlishaw */
  6. /*                                                                   */
  7. /* This routine, when linked into an .EXE instead of the usual C     */
  8. /* runtime, sends the edited result string to a named queue (if      */
  9. /* it exists).  If the queue does not exist, then all printf data    */
  10. /* are discarded (ignored).                                          */
  11. /*                                                                   */
  12. /* The result string is accumulated until a line feed (LF) character */
  13. /* is received; the whole line is then sent to the queue.  Lines are */
  14. /* automatically broken at a set (tailorable) length, if necessary.  */
  15. /*                                                                   */
  16. /* This routine may be tailored by altering the #defines at the      */
  17. /* top:                                                              */
  18. /*                                                                   */
  19. /*   PRINTFID      - An ID string that is prefixed to each line of   */
  20. /*                   data before being sent to the queue.  This      */
  21. /*                   can be any string, or the null string.          */
  22. /*   PRINTFMAXLEN  - Maximum length of string that can be formatted  */
  23. /*                   in a single call.                               */
  24. /*                   Results are unpredictable if this length is     */
  25. /*                   exceeded.  Default is 250.                      */
  26. /*   PRINTFLINELEN - Maximum length of a line that will be sent.     */
  27. /*                   This excludes the prefix and its blank.  If the */
  28. /*                   calls to printf cause a line to be generated    */
  29. /*                   that is longer than this, the line will be      */
  30. /*                   broken at this point.                           */
  31. /*   PRINTFTHREADS - Maximum number of threads expected.  This may   */
  32. /*                   need to be increased if the process limitation  */
  33. /*                   is removed, or you can save a little storage    */
  34. /*                   by decreasing it.  PRINTFs from threads larger  */
  35. /*                   than this number are ignored.                   */
  36. /*   PRINTFQNAME   - The name of the public queue that the result    */
  37. /*                   is to be sent to.  Normally '\QUEUES\PRINTF32'. */
  38. /*                   Note that the \QUEUES\ part is required.        */
  39. /*                                                                   */
  40. /* Returns:                                                          */
  41. /*   n: Count of data characters, if successfully received           */
  42. /*   0: If no queue existed (i.e., no server)                        */
  43. /*  <0: An error occurred (e.g., out of memory)                      */
  44. /*                                                                   */
  45. /* Restrictions:                                                     */
  46. /*   1. Total length of data (length of PRINTFID, + PRINTFMAXLEN)    */
  47. /*      must be less than 32K-1.                                     */
  48. /*   2. This has only been tested under IBM C Set/2 compiler.  It    */
  49. /*      may need modification for other compilers.                   */
  50. /*   3. This version uses a static array to point to the per-thread  */
  51. /*      data.  The code could be made read-only by hanging this      */
  52. /*      array (and the other static information) off a system-owned  */
  53. /*      anchor of some kind.                                         */
  54. /*   4. To use PRINTF within other than the main thread in a         */
  55. /*      program, that thread must be started with _beginthread       */
  56. /*      (not DosCreateThread).  This restriction is a consequence of */
  57. /*      the use of C library routines (sprintf) in PRINTF, and may   */
  58. /*      not apply to all compilers.                                  */
  59. /*   5. If the last PRINTF done by a thread does not end in '\n'     */
  60. /*      then the final part-line may be lost, or appear later.       */
  61. /*                                                                   */
  62. /* Protocol:                                                         */
  63. /*   PRINTF writes its data to the named queue using the following   */
  64. /*   protocol:                                                       */
  65. /*     Address -- Holds the address of the string to be sent. This   */
  66. /*                is a 0-terminated string) starting at offset 0.    */
  67. /*     Length  -- The length of the data, including terminator.      */
  68. /*                A negative length indicates a BELL in the data.    */
  69. /*     Request -- Timestamp (when queue was written) in C long       */
  70. /*                integer format (as returned by time()).            */
  71. /*                This may be 0L if not required.                    */
  72. /*                                                                   */
  73. /* Notes:                                                            */
  74. /*   1. PMPRINTF uses a queue and shared memory messages because:    */
  75. /*        (a) It makes collection at the receiving end very easy.    */
  76. /*        (b) I wanted to experiment with queues and shared memory.  */
  77. /*      This make not be the most cost-effective method.             */
  78. /*   2. Typical IBM C Set/2 compiler invocation:                     */
  79. /*        icc /c /Gm /O+ /Q /J /Kabgop                               */
  80. /*      If you get linking errors (duplicate symbols, etc.), try     */
  81. /*      recompiling PRINTF.C with the same options as you use for    */
  82. /*      your main program.                                           */
  83. /*   3. PRINTF sends the timestamp across the queue as a GMT long    */
  84. /*      integer, the result from a call to the C function time().    */
  85. /*      This will only be correct if the environment variable TZ has */
  86. /*      been set (e.g., TZ=EST5EDT), or you are in the same time     */
  87. /*      zone as the default for your compiler.                       */
  88. /*      For more information, see the tzset() function description   */
  89. /*      in your C compiler manual.                                   */
  90.  
  91. /* ----- Customization variables ----- */
  92. #define PRINTFID      ""
  93. #define PRINTFMAXLEN  300
  94. #define PRINTFLINELEN 100
  95. #define PRINTFTHREADS  54
  96. #define PRINTFQNAME   "\\QUEUES\\PRINTF32"
  97.  
  98. /* ----- Includes and externals ----- */
  99. #include <stdlib.h>                /* standard C functions */
  100. #include <stddef.h>                /* .. */
  101. #include <string.h>                /* .. */
  102. #include <time.h>                  /* .. */
  103. #include <stdarg.h>                /* .. */
  104. #include <stdio.h>                 /* (needed to pick up real name) */
  105. #define INCL_DOS                   /* Operating system definitions */
  106. #include <os2.h>                   /* For OS/2 functions */
  107.  
  108. /* ----- Local defines ----- */
  109. #define PRINTFIDSIZE sizeof(PRINTFID)
  110. #define PRINTFMAXBUF PRINTFIDSIZE+PRINTFLINELEN
  111. #define getmax(a,b)    (((a) > (b)) ? (a) : (b))
  112.  
  113. /* ----- Per-thread output buffer and current indices into line ---- */
  114. struct perthread {
  115.   LONG   lineindex;                /* where next char */
  116.   LONG   tidemark;                 /* rightmost char */
  117.   int    bell;                     /* TRUE if line has bell */
  118.   UCHAR  line[PRINTFMAXBUF];       /* accumulator */
  119.   };
  120.  
  121. /* ----- Local static variables ----- */
  122. static ULONG  ourpid=0;            /* our process ID */
  123. static ULONG  servepid=0;          /* process IDs of the server */
  124. static HQUEUE qhandle=0;           /* handle for the queue */
  125. static struct perthread *tps[PRINTFTHREADS+1]; /* -> per-thread data */
  126.  
  127. /* ----- Local subroutine ----- */
  128. static int printf_(struct perthread *);
  129.  
  130. /* ----------------------------------------------------------------- */
  131. /* The "printf" function.  Note this has a variable number of        */
  132. /* arguments.                                                        */
  133. /* ----------------------------------------------------------------- */
  134. //#ifdef __BORLANDC__
  135. int printf(const char *f, ...)
  136. //#else
  137. //int printf(const unsigned char *f)
  138. //#endif
  139.   {
  140.   TIB    *ptib;                    /* process/thread id structures */
  141.   PIB    *ppib;                    /* .. */
  142.   TID    ourtid;                   /* thread ID */
  143.   struct perthread *tp;            /* pointer to per-thread data */
  144.   int    rc;                       /* returncode */
  145.   ULONG  urc;                      /* returncode */
  146.  
  147.   urc=DosOpenQueue(&servepid, &qhandle, PRINTFQNAME);  /* Open the Q */
  148.   /* Non-0 RC means Q does not exist or cannot be opened */
  149.   if (urc==343) return 0;          /* queue does not exist, so quit */
  150.   if (urc!=0)   return -1;         /* report any other error */
  151.  
  152.   /* First determine our thread ID (and hence get access to the      */
  153.   /* correct per-thread data.  If the per-thread data has not been   */
  154.   /* allocated, then allocate it now.  It is never freed, once       */
  155.   /* allocated, as PRINTF is not notified of end-of-thread.          */
  156.   DosGetInfoBlocks(&ptib,&ppib);   /* get process/thread info */
  157.   ourtid=ptib->tib_ptib2->tib2_ultid;   /* .. and copy TID */
  158.   if (ourtid>PRINTFTHREADS)        /* too many threads .. */
  159.     return 0;                      /* .. so quit, quietly */
  160.   tp=tps[ourtid];                  /* copy to local pointer */
  161.   if (tp==NULL) {                  /* uninitialized (NULL=0) */
  162.     /* allocate a per-thread structure */
  163.     tp=(struct perthread *)malloc(sizeof(struct perthread));
  164.     if (tp==NULL) return -1;       /* out of memory -- return error */
  165.     tps[ourtid]=tp;                /* save for future calls */
  166.     strcpy(tp->line,PRINTFID);     /* initialize: line.. */
  167.     tp->lineindex=PRINTFIDSIZE-1;  /* ..where next char */
  168.     tp->tidemark =PRINTFIDSIZE-2;  /* ..rightmost char */
  169.     tp->bell=FALSE;                /* ..if line has bell */
  170.     if (ourpid==0) ourpid=ppib->pib_ulpid;   /* save PID for all to use */
  171.     }
  172.  
  173.   { /* Block for declarations -- only needed if queue exists, etc. */
  174.     LONG  count;                   /* count of characters formatted */
  175.     UCHAR buffer[PRINTFMAXLEN+1];  /* formatting area */
  176.     LONG  i, newind;               /* work */
  177.     UCHAR ch;                      /* .. */
  178.     va_list argptr;                /* -> variable argument list */
  179.  
  180.     va_start(argptr, f);           /* get pointer to argument list */
  181.     count=vsprintf(buffer, f, argptr);
  182.     va_end(argptr);                /* done with variable arguments */
  183.  
  184.     if (count<0) return count-1000;/* bad start */
  185.  
  186.     if (count>PRINTFMAXLEN) {
  187.       /* Disaster -- we are probably "dead", but just in case we */
  188.       /* are not, carry on with truncated data. */
  189.       count=PRINTFMAXLEN;
  190.       }
  191.     buffer[count]='\0';            /* ensure terminated */
  192.     /* OK, ready to go with the data now in BUFFER                    */
  193.     /* We copy from the formatted string to the output (line) buffer, */
  194.     /* taking note of certain control characters and sending a line   */
  195.     /* the queue whenever we see a LF control, or when the line       */
  196.     /* fills (causing a forced break).                                */
  197.     for (i=0; ; i++) {
  198.       ch=buffer[i]; if (!ch) break;
  199.       switch(ch) {
  200.         case '\r':                 /* carriage return */
  201.           tp->lineindex=PRINTFIDSIZE-1; /* back to start of line */
  202.           break;
  203.         case '\n':                 /* new line */
  204.         case '\f':                 /* form feed */
  205.       rc=printf_(tp);          /* print a line */
  206.           if (rc!=0) return rc;    /* error */
  207.           break;
  208.         case '\t':                 /* tab */
  209.           newind=tp->lineindex-PRINTFIDSIZE+1;   /* offset into data */
  210.           newind=tp->lineindex+5-newind%5;    /* new index requested */
  211.           if (newind>=PRINTFMAXBUF) newind=PRINTFMAXBUF;    /* clamp */
  212.           for (; tp->lineindex<newind; tp->lineindex++) {
  213.             if (tp->lineindex>tp->tidemark) {  /* beyond current end */
  214.               tp->line[tp->lineindex]=' ';              /* add space */
  215.               tp->tidemark=tp->lineindex;
  216.               }
  217.             }
  218.           break;
  219.         case '\v':                 /* vertical tab */
  220.           /* ignore it */
  221.           break;
  222.         case '\b':                 /* backspace */
  223.       tp->lineindex=getmax(tp->lineindex-1,PRINTFIDSIZE);
  224.           break;
  225.         case '\a':                 /* alert (bell) */
  226.           tp->bell=TRUE;
  227.           break;
  228.         default:                   /* ordinary character */
  229.           tp->line[tp->lineindex]=ch;
  230.           if (tp->lineindex>tp->tidemark)  /* is rightmost.. */
  231.             tp->tidemark=tp->lineindex;
  232.           tp->lineindex++;                 /* step for next */
  233.         } /* switch */
  234.       if (tp->lineindex>=PRINTFMAXBUF) {
  235.         rc=printf_(tp);            /* print a line */
  236.         if (rc!=0) return rc;      /* error */
  237.         }
  238.  
  239.       } /* copy loop */
  240.     return count;                  /* all formatted data processed */
  241.     } /* block */
  242.   } /* printf */
  243.  
  244. /* ----- printf_(tp) -- Local subroutine to send a line ------------ */
  245. /* A line has been completed (or overflowed): write it to the queue. */
  246. int printf_(struct perthread *tp)  /* pointer to per-thread data */
  247.   {
  248.   ULONG   urc;                     /* unsigned returncode */
  249.   PSZ     pszTo, pszFrom;          /* character pointers */
  250.   PVOID   addr;                    /* address of output data */
  251.   long    size;                    /* total size of output data */
  252.   time_t  timenow;                 /* holds current time */
  253.  
  254.   tp->line[tp->tidemark+1]='\0';   /* add terminator */
  255.   size=tp->tidemark+2;             /* total length of data */
  256.  
  257.   /* Get some shared memory that can be given away */
  258.   urc=DosAllocSharedMem(&addr, NULL, (unsigned)size,
  259.     OBJ_GIVEABLE|PAG_WRITE|PAG_COMMIT);
  260.   if (urc!=0) return -2;           /* error */
  261.  
  262.   pszTo=addr;                      /* copy for clarity */
  263.   pszFrom=&(tp->line[0]);          /* pointer to source */
  264.   strcpy(pszTo,pszFrom);           /* copy the string to shared memory */
  265.  
  266.   if (ourpid!=servepid) {          /* (no giveaway needed if to self) */
  267.     urc=DosGiveSharedMem(addr, servepid, PAG_READ); /* give access */
  268.     if (urc!=0) return -3;}        /* error */
  269.  
  270.   /* Write the selector, size, and timestamp to the queue */
  271.   if (tp->bell) size=-size;        /* BELL passed by negation */
  272.   time(&timenow);                  /* optional - else use 0 */
  273.   urc=DosWriteQueue(qhandle,       /* handle */
  274.          (unsigned)timenow,        /* 'request' (timestamp) */
  275.          (unsigned)size,           /* 'length'  (length/bell) */
  276.                    addr,           /* 'address' (address) */
  277.                    0);             /* priority (FIFO if enabled) */
  278.   if (urc!=0) return -4;           /* error */
  279.   if (ourpid!=servepid) {          /* if given away.. */
  280.     urc=DosFreeMem(addr);          /* .. *we* are done with it */
  281.     if (urc!=0) return -5;}        /* error */
  282.   /* Reset the line buffer and indices */
  283.   tp->lineindex=PRINTFIDSIZE-1;    /* where next char */
  284.   tp->tidemark =PRINTFIDSIZE-2;    /* rightmost char */
  285.   tp->bell     =FALSE;             /* true if line has bell */
  286.   return 0;                        /* success! */
  287.   } /* printf_ */
  288.