This is Unofficial EPICS BASE Doxygen Site
printfRecord.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * EPICS BASE is distributed subject to a Software License Agreement found
5 * in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7 
8 /* Printf record type */
9 /*
10  * Author: Andrew Johnson
11  * Date: 2012-09-18
12  */
13 
14 #include <stddef.h>
15 #include <string.h>
16 
17 #include "dbDefs.h"
18 #include "errlog.h"
19 #include "alarm.h"
20 #include "cantProceed.h"
21 #include "dbAccess.h"
22 #include "dbEvent.h"
23 #include "dbFldTypes.h"
24 #include "epicsMath.h"
25 #include "epicsStdio.h"
26 #include "errMdef.h"
27 #include "recSup.h"
28 #include "recGbl.h"
29 #include "special.h"
30 #define GEN_SIZE_OFFSET
31 #include "printfRecord.h"
32 #undef GEN_SIZE_OFFSET
33 #include "epicsExport.h"
34 
35 
36 /* Flag bits */
37 #define F_CHAR 1
38 #define F_SHORT 2
39 #define F_LONG 4
40 #define F_LONGLONG 8
41 #define F_LEFT 0x10
42 #define F_BADFMT 0x40
43 #define F_BADLNK 0x80
44 #define F_BAD (F_BADFMT | F_BADLNK)
45 
46 #define GET_PRINT(VALTYPE, DBRTYPE) \
47  VALTYPE val; \
48  int ok; \
49 \
50  if (dbLinkIsConstant(plink)) \
51  ok = recGblInitConstantLink(plink++, DBRTYPE, &val); \
52  else \
53  ok = ! dbGetLink(plink++, DBRTYPE, &val, 0, 0); \
54  if (ok) \
55  added = epicsSnprintf(pval, vspace + 1, format, val); \
56  else \
57  flags |= F_BADLNK
58 
59 static void doPrintf(printfRecord *prec)
60 {
61  const char *pfmt = prec->fmt;
62  DBLINK *plink = &prec->inp0;
63  int linkn = 0;
64  char *pval = prec->val;
65  int vspace = prec->sizv - 1;
66  int ch;
67 
68  while (vspace > 0 && (ch = *pfmt++)) {
69  if (ch != '%') {
70  /* Copy literal strings directly into prec->val */
71  *pval++ = ch;
72  --vspace;
73  }
74  else {
75  char format[20];
76  char *pformat = format;
77  int width = 0;
78  int precision = 0;
79  int *pnum = &width;
80  int flags = 0;
81  int added = 0;
82  int cont = 1;
83 
84  /* The format directive parsing here is not comprehensive,
85  * in most cases we just copy each directive into format[]
86  * and get epicsSnprintf() do all the work. We do replace
87  * all variable-length field width or precision '*' chars
88  * with an integer read from the next input link, and we
89  * also convert %ls (long string) directives ourself, so
90  * we need to know the width, precision and justification.
91  */
92 
93  *pformat++ = ch; /* '%' */
94  while (cont && (ch = *pfmt++)) {
95  *pformat++ = ch;
96  switch (ch) {
97  case '+': case ' ': case '#':
98  break;
99  case '-':
100  flags |= F_LEFT;
101  break;
102  case '.':
103  pnum = &precision;
104  break;
105  case '0': case '1': case '2': case '3': case '4':
106  case '5': case '6': case '7': case '8': case '9':
107  *pnum = *pnum * 10 + ch - '0';
108  break;
109  case '*':
110  if (*pnum) {
111  flags |= F_BADFMT;
112  }
113  else if (linkn++ < PRINTF_NLINKS) {
114  epicsInt16 i;
115  int ok;
116 
117  if (dbLinkIsConstant(plink))
118  ok = recGblInitConstantLink(plink++, DBR_SHORT, &i);
119  else
120  ok = ! dbGetLink(plink++, DBR_SHORT, &i, 0, 0);
121  if (ok) {
122  *pnum = i;
123  added = epicsSnprintf(--pformat, 6, "%d", i);
124  pformat += added;
125  }
126  else /* No more LNKn fields */
127  flags |= F_BADLNK;
128  }
129  else
130  flags |= F_BADLNK;
131  break;
132  case 'h':
133  if (flags & (F_LONGLONG | F_LONG | F_CHAR))
134  flags |= F_BADFMT;
135  else if (flags & F_SHORT)
136  flags = (flags & ~F_SHORT) | F_CHAR;
137  else
138  flags |= F_SHORT;
139  break;
140  case 'l':
141  if (flags & (F_LONGLONG | F_SHORT | F_CHAR))
142  flags |= F_BADFMT;
143  else if (flags & F_LONG)
144  flags = (flags & ~F_LONG) | F_LONGLONG;
145  else
146  flags |= F_LONG;
147  break;
148  default:
149  if (strchr("diouxXeEfFgGcs%", ch) == NULL)
150  flags |= F_BADFMT;
151  cont = 0;
152  break;
153  }
154  }
155  if (!ch) /* End of format string */
156  break;
157 
158  if (flags & F_BAD)
159  goto bad_format;
160 
161  *pformat = 0; /* Terminate our format string */
162 
163  if (width < 0) {
164  width = -width;
165  flags |= F_LEFT;
166  }
167  if (precision < 0)
168  precision = 0;
169 
170  if (ch == '%') {
171  added = epicsSnprintf(pval, vspace + 1, "%s", format);
172  }
173  else if (linkn++ >= PRINTF_NLINKS) {
174  /* No more LNKn fields */
175  flags |= F_BADLNK;
176  }
177  else
178  switch (ch) { /* Conversion character */
179  case 'c': case 'd': case 'i':
180  if (ch == 'c' || flags & F_CHAR) {
182  }
183  else if (flags & F_SHORT) {
185  }
186  else if (flags & F_LONGLONG) {
188  }
189  else { /* F_LONG has no real effect */
191  }
192  break;
193 
194  case 'o': case 'x': case 'X': case 'u':
195  if (flags & F_CHAR) {
197  }
198  else if (flags & F_SHORT) {
200  }
201  else if (flags & F_LONGLONG) {
203  }
204  else { /* F_LONG has no real effect */
206  }
207  break;
208 
209  case 'e': case 'E':
210  case 'f': case 'F':
211  case 'g': case 'G':
212  if (flags & F_SHORT) {
214  }
215  else {
217  }
218  break;
219 
220  case 's':
221  if (flags & F_LONG) {
222  long n = vspace + 1;
223  long status;
224 
225  if (precision && n > precision)
226  n = precision + 1;
227  /* If set, precision is the maximum number of
228  * characters to be printed from the string.
229  * It does not limit the field width however.
230  */
231  if (dbLinkIsConstant(plink)) {
232  epicsUInt32 len = n;
233  status = dbLoadLinkLS(plink++, pval, n, &len);
234  n = len;
235  }
236  else
237  status = dbGetLink(plink++, DBR_CHAR, pval, 0, &n);
238  if (status)
239  flags |= F_BADLNK;
240  else {
241  int padding;
242 
243  /* Terminate string and measure its length */
244  pval[n] = 0;
245  added = strlen(pval);
246  padding = width - added;
247 
248  if (padding > 0) {
249  if (flags & F_LEFT) {
250  /* add spaces on RHS */
251  if (width > vspace)
252  padding = vspace - added;
253  memset(pval + added, ' ', padding);
254  }
255  else {
256  /* insert spaces on LHS */
257  int trunc = width - vspace;
258 
259  if (trunc < added) {
260  added -= trunc;
261  memmove(pval + padding, pval, added);
262  }
263  else {
264  padding = vspace;
265  added = 0;
266  }
267  memset(pval, ' ', padding);
268  }
269  added += padding;
270  }
271  }
272  }
273  else {
274  char val[MAX_STRING_SIZE];
275  int ok;
276 
277  if (dbLinkIsConstant(plink))
278  ok = recGblInitConstantLink(plink++, DBR_STRING, val);
279  else
280  ok = ! dbGetLink(plink++, DBR_STRING, val, 0, 0);
281  if (ok)
282  added = epicsSnprintf(pval, vspace + 1, format, val);
283  else
284  flags |= F_BADLNK;
285  }
286  break;
287 
288  default:
289  errlogPrintf("printfRecord: Unexpected conversion '%s'\n",
290  format);
291  flags |= F_BADFMT;
292  break;
293  }
294 
295  if (flags & F_BAD) {
296  bad_format:
297  added = epicsSnprintf(pval, vspace + 1, "%s",
298  flags & F_BADLNK ? prec->ivls : format);
299  }
300 
301  if (added <= vspace) {
302  pval += added;
303  vspace -= added;
304  }
305  else {
306  /* Output was truncated */
307  pval += vspace;
308  vspace = 0;
309  }
310  }
311  }
312  *pval++ = 0; /* Terminate the VAL string */
313  prec->len = pval - prec->val;
314 }
315 
316 
317 static long init_record(struct dbCommon *pcommon, int pass)
318 {
319  struct printfRecord *prec = (struct printfRecord *)pcommon;
320  printfdset *pdset;
321 
322  if (pass == 0) {
323  size_t sizv = prec->sizv;
324 
325  if (sizv < 16) {
326  sizv = 16; /* Enforce a minimum size for the VAL field */
327  prec->sizv = sizv;
328  }
329 
330  prec->val = callocMustSucceed(1, sizv, "printf::init_record");
331  prec->len = 0;
332  return 0;
333  }
334 
335  pdset = (printfdset *) prec->dset;
336  if (!pdset)
337  return 0; /* Device support is optional */
338 
339  if (pdset->common.number < 5) {
340  recGblRecordError(S_dev_missingSup, prec, "printf::init_record");
341  return S_dev_missingSup;
342  }
343 
344  if (pdset->common.init_record) {
345  long status = pdset->common.init_record(pcommon);
346  if (status)
347  return status;
348  }
349 
350  return 0;
351 }
352 
353 static long process(struct dbCommon *pcommon)
354 {
355  struct printfRecord *prec = (struct printfRecord *)pcommon;
356  int pact = prec->pact;
357  printfdset *pdset;
358  long status = 0;
359  epicsUInt16 events;
360 
361  if (!pact) {
362  doPrintf(prec);
363 
364  prec->udf = FALSE;
365  recGblGetTimeStamp(prec);
366  }
367 
368  /* Call device support */
369  pdset = (printfdset *) prec->dset;
370  if (pdset &&
371  pdset->common.number >= 5 &&
372  pdset->write_string) {
373  status = pdset->write_string(prec);
374 
375  /* Asynchronous if device support set pact */
376  if (!pact && prec->pact)
377  return status;
378  }
379 
380  prec->pact = TRUE;
381 
382  /* Post monitor */
383  events = recGblResetAlarms(prec);
384  db_post_events(prec, prec->val, events | DBE_VALUE | DBE_LOG);
385  db_post_events(prec, &prec->len, events | DBE_VALUE | DBE_LOG);
386 
387  /* Wrap up */
388  recGblFwdLink(prec);
389  prec->pact = FALSE;
390  return status;
391 }
392 
393 static long cvt_dbaddr(DBADDR *paddr)
394 {
395  printfRecord *prec = (printfRecord *)paddr->precord;
396  int fieldIndex = dbGetFieldIndex(paddr);
397 
398  if (fieldIndex == printfRecordVAL) {
399  paddr->pfield = prec->val;
400  paddr->no_elements = 1;
401  paddr->field_type = DBF_STRING;
402  paddr->dbr_field_type = DBF_STRING;
403  paddr->field_size = prec->sizv;
404  }
405  else
406  errlogPrintf("printfRecord::cvt_dbaddr called for %s.%s\n",
407  prec->name, paddr->pfldDes->name);
408  return 0;
409 }
410 
411 static long get_array_info(DBADDR *paddr, long *no_elements, long *offset)
412 {
413  printfRecord *prec = (printfRecord *) paddr->precord;
414 
415  *no_elements = prec->len;
416  *offset = 0;
417  return 0;
418 }
419 
420 
421 /* Create Record Support Entry Table */
422 
423 #define report NULL
424 #define initialize NULL
425 /* init_record */
426 /* process */
427 #define special NULL
428 #define get_value NULL
429 /* cvt_dbaddr */
430 /* get_array_info */
431 #define put_array_info NULL
432 #define get_units NULL
433 #define get_precision NULL
434 #define get_enum_str NULL
435 #define get_enum_strs NULL
436 #define put_enum_str NULL
437 #define get_graphic_double NULL
438 #define get_control_double NULL
439 #define get_alarm_double NULL
440 
442  RSETNUMBER,
443  report,
444  initialize,
445  init_record,
446  process,
447  special,
448  get_value,
449  cvt_dbaddr,
452  get_units,
454  get_enum_str,
456  put_enum_str,
460 };
461 epicsExportAddress(rset, printfRSET);
462 
#define DBR_CHAR
Definition: db_access.h:74
#define F_BADFMT
Definition: printfRecord.c:42
#define get_enum_strs
Definition: printfRecord.c:435
#define RSETNUMBER
Definition: recSup.h:92
#define DBR_STRING
Definition: db_access.h:69
#define FALSE
Definition: dbDefs.h:32
double epicsFloat64
Definition: epicsTypes.h:49
pvd::Status status
#define F_LONG
Definition: printfRecord.c:39
int i
Definition: scan.c:967
#define F_LONGLONG
Definition: printfRecord.c:40
epicsExportAddress(rset, printfRSET)
#define DBR_USHORT
Definition: dbFldTypes.h:80
unsigned short epicsUInt16
Definition: epicsTypes.h:41
#define init_record
#define DBR_FLOAT
Definition: db_access.h:72
#define S_dev_missingSup
Definition: devSup.h:170
#define DBR_INT64
Definition: dbFldTypes.h:83
#define get_enum_str
Definition: printfRecord.c:434
#define F_BAD
Definition: printfRecord.c:44
unsigned char epicsUInt8
Definition: epicsTypes.h:39
#define get_value
Definition: printfRecord.c:428
#define NULL
Definition: catime.c:38
unsigned int epicsUInt32
Definition: epicsTypes.h:43
#define put_array_info
Definition: printfRecord.c:431
Miscellaneous macro definitions.
#define get_precision
Definition: printfRecord.c:433
#define DBR_UINT64
Definition: dbFldTypes.h:84
unsigned long long epicsUInt64
Definition: epicsTypes.h:45
#define get_graphic_double
Definition: printfRecord.c:437
#define DBR_UCHAR
Definition: dbFldTypes.h:78
#define get_units
Definition: printfRecord.c:432
#define DBE_VALUE
Definition: caeventmask.h:38
#define get_array_info
Definition: aiRecord.c:56
rset printfRSET
Definition: printfRecord.c:441
float epicsFloat32
Definition: epicsTypes.h:48
#define DBE_LOG
Definition: caeventmask.h:40
#define get_alarm_double
Definition: printfRecord.c:439
#define DBR_DOUBLE
Definition: db_access.h:76
#define DBR_SHORT
Definition: db_access.h:71
char epicsInt8
Definition: epicsTypes.h:38
#define F_LEFT
Definition: printfRecord.c:41
LIBCOM_API void * callocMustSucceed(size_t count, size_t size, const char *msg)
A calloc() that never returns NULL.
Definition: cantProceed.c:22
#define put_enum_str
Definition: printfRecord.c:436
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
#define report
Definition: printfRecord.c:423
#define special
Definition: printfRecord.c:427
#define DBR_LONG
Definition: db_access.h:75
#define F_CHAR
Definition: printfRecord.c:37
#define MAX_STRING_SIZE
Definition: epicsTypes.h:65
#define TRUE
Definition: dbDefs.h:27
if(yy_init)
Definition: scan.c:972
#define GET_PRINT(VALTYPE, DBRTYPE)
Definition: printfRecord.c:46
Definition: recSup.h:67
Routines for code that can&#39;t continue or return after an error.
short epicsInt16
Definition: epicsTypes.h:40
int prec
Definition: reader.c:29
#define F_SHORT
Definition: printfRecord.c:38
#define cvt_dbaddr
Definition: aiRecord.c:55
#define DBR_ULONG
Definition: dbFldTypes.h:82
int epicsInt32
Definition: epicsTypes.h:42
LIBCOM_API int epicsStdCall epicsSnprintf(char *str, size_t size, const char *format,...) EPICS_PRINTF_STYLE(3
#define get_control_double
Definition: printfRecord.c:438
long long epicsInt64
Definition: epicsTypes.h:44
#define F_BADLNK
Definition: printfRecord.c:43
#define initialize
Definition: printfRecord.c:424
Exporting IOC objects.