| /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */ |
| #include <stdarg.h> |
| #include <string.h> |
| #include "plan9.h" |
| #include "fmt.h" |
| #include "fmtdef.h" |
| |
| /* |
| * How many bytes of output UTF will be produced by quoting (if necessary) this string? |
| * How many runes? How much of the input will be consumed? |
| * The parameter q is filled in by __quotesetup. |
| * The string may be UTF or Runes (s or r). |
| * Return count does not include NUL. |
| * Terminate the scan at the first of: |
| * NUL in input |
| * count exceeded in input |
| * count exceeded on output |
| * *ninp is set to number of input bytes accepted. |
| * nin may be <0 initially, to avoid checking input by count. |
| */ |
| void |
| __quotesetup(char *s, Rune *r, int nin, int nout, Quoteinfo *q, int sharp, int runesout) |
| { |
| int w; |
| Rune c; |
| |
| q->quoted = 0; |
| q->nbytesout = 0; |
| q->nrunesout = 0; |
| q->nbytesin = 0; |
| q->nrunesin = 0; |
| if(sharp || nin==0 || (s && *s=='\0') || (r && *r=='\0')){ |
| if(nout < 2) |
| return; |
| q->quoted = 1; |
| q->nbytesout = 2; |
| q->nrunesout = 2; |
| } |
| for(; nin!=0; nin--){ |
| if(s) |
| w = chartorune(&c, s); |
| else{ |
| c = *r; |
| w = runelen(c); |
| } |
| |
| if(c == '\0') |
| break; |
| if(runesout){ |
| if(q->nrunesout+1 > nout) |
| break; |
| }else{ |
| if(q->nbytesout+w > nout) |
| break; |
| } |
| |
| if((c <= L' ') || (c == L'\'') || (fmtdoquote!=nil && fmtdoquote(c))){ |
| if(!q->quoted){ |
| if(runesout){ |
| if(1+q->nrunesout+1+1 > nout) /* no room for quotes */ |
| break; |
| }else{ |
| if(1+q->nbytesout+w+1 > nout) /* no room for quotes */ |
| break; |
| } |
| q->nrunesout += 2; /* include quotes */ |
| q->nbytesout += 2; /* include quotes */ |
| q->quoted = 1; |
| } |
| if(c == '\'') { |
| if(runesout){ |
| if(1+q->nrunesout+1 > nout) /* no room for quotes */ |
| break; |
| }else{ |
| if(1+q->nbytesout+w > nout) /* no room for quotes */ |
| break; |
| } |
| q->nbytesout++; |
| q->nrunesout++; /* quotes reproduce as two characters */ |
| } |
| } |
| |
| /* advance input */ |
| if(s) |
| s += w; |
| else |
| r++; |
| q->nbytesin += w; |
| q->nrunesin++; |
| |
| /* advance output */ |
| q->nbytesout += w; |
| q->nrunesout++; |
| |
| #ifndef PLAN9PORT |
| /* ANSI requires precision in bytes, not Runes. */ |
| nin-= w-1; /* and then n-- in the loop */ |
| #endif |
| } |
| } |
| |
| static int |
| qstrfmt(char *sin, Rune *rin, Quoteinfo *q, Fmt *f) |
| { |
| Rune r, *rm, *rme; |
| char *t, *s, *m, *me; |
| Rune *rt, *rs; |
| ulong fl; |
| int nc, w; |
| |
| m = sin; |
| me = m + q->nbytesin; |
| rm = rin; |
| rme = rm + q->nrunesin; |
| |
| fl = f->flags; |
| w = 0; |
| if(fl & FmtWidth) |
| w = f->width; |
| if(f->runes){ |
| if(!(fl & FmtLeft) && __rfmtpad(f, w - q->nrunesout) < 0) |
| return -1; |
| }else{ |
| if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0) |
| return -1; |
| } |
| t = (char*)f->to; |
| s = (char*)f->stop; |
| rt = (Rune*)f->to; |
| rs = (Rune*)f->stop; |
| if(f->runes) |
| FMTRCHAR(f, rt, rs, '\''); |
| else |
| FMTRUNE(f, t, s, '\''); |
| for(nc = q->nrunesin; nc > 0; nc--){ |
| if(sin){ |
| r = *(uchar*)m; |
| if(r < Runeself) |
| m++; |
| else if((me - m) >= UTFmax || fullrune(m, me-m)) |
| m += chartorune(&r, m); |
| else |
| break; |
| }else{ |
| if(rm >= rme) |
| break; |
| r = *(uchar*)rm++; |
| } |
| if(f->runes){ |
| FMTRCHAR(f, rt, rs, r); |
| if(r == '\'') |
| FMTRCHAR(f, rt, rs, r); |
| }else{ |
| FMTRUNE(f, t, s, r); |
| if(r == '\'') |
| FMTRUNE(f, t, s, r); |
| } |
| } |
| |
| if(f->runes){ |
| FMTRCHAR(f, rt, rs, '\''); |
| USED(rs); |
| f->nfmt += rt - (Rune *)f->to; |
| f->to = rt; |
| if(fl & FmtLeft && __rfmtpad(f, w - q->nrunesout) < 0) |
| return -1; |
| }else{ |
| FMTRUNE(f, t, s, '\''); |
| USED(s); |
| f->nfmt += t - (char *)f->to; |
| f->to = t; |
| if(fl & FmtLeft && __fmtpad(f, w - q->nbytesout) < 0) |
| return -1; |
| } |
| return 0; |
| } |
| |
| int |
| __quotestrfmt(int runesin, Fmt *f) |
| { |
| int nin, outlen; |
| Rune *r; |
| char *s; |
| Quoteinfo q; |
| |
| nin = -1; |
| if(f->flags&FmtPrec) |
| nin = f->prec; |
| if(runesin){ |
| r = va_arg(f->args, Rune *); |
| s = nil; |
| }else{ |
| s = va_arg(f->args, char *); |
| r = nil; |
| } |
| if(!s && !r) |
| return __fmtcpy(f, (void*)"<nil>", 5, 5); |
| |
| if(f->flush) |
| outlen = 0x7FFFFFFF; /* if we can flush, no output limit */ |
| else if(f->runes) |
| outlen = (Rune*)f->stop - (Rune*)f->to; |
| else |
| outlen = (char*)f->stop - (char*)f->to; |
| |
| __quotesetup(s, r, nin, outlen, &q, f->flags&FmtSharp, f->runes); |
| /*print("bytes in %d bytes out %d runes in %d runesout %d\n", q.nbytesin, q.nbytesout, q.nrunesin, q.nrunesout); */ |
| |
| if(runesin){ |
| if(!q.quoted) |
| return __fmtrcpy(f, r, q.nrunesin); |
| return qstrfmt(nil, r, &q, f); |
| } |
| |
| if(!q.quoted) |
| return __fmtcpy(f, s, q.nrunesin, q.nbytesin); |
| return qstrfmt(s, nil, &q, f); |
| } |
| |
| int |
| quotestrfmt(Fmt *f) |
| { |
| return __quotestrfmt(0, f); |
| } |
| |
| int |
| quoterunestrfmt(Fmt *f) |
| { |
| return __quotestrfmt(1, f); |
| } |
| |
| void |
| quotefmtinstall(void) |
| { |
| fmtinstall('q', quotestrfmt); |
| fmtinstall('Q', quoterunestrfmt); |
| } |
| |
| int |
| __needsquotes(char *s, int *quotelenp) |
| { |
| Quoteinfo q; |
| |
| __quotesetup(s, nil, -1, 0x7FFFFFFF, &q, 0, 0); |
| *quotelenp = q.nbytesout; |
| |
| return q.quoted; |
| } |
| |
| int |
| __runeneedsquotes(Rune *r, int *quotelenp) |
| { |
| Quoteinfo q; |
| |
| __quotesetup(nil, r, -1, 0x7FFFFFFF, &q, 0, 0); |
| *quotelenp = q.nrunesout; |
| |
| return q.quoted; |
| } |