blob: e6b91e34b35b81c4b9642dcc51c869d52918eeed [file] [log] [blame]
/*
* The authors of this software are Rob Pike and Ken Thompson.
* Copyright (c) 2002 by Lucent Technologies.
* Permission to use, copy, modify, and distribute this software for any
* purpose without fee is hereby granted, provided that this entire notice
* is included in all copies of any software which is or includes a copy
* or modification of this software and in all copies of the supporting
* documentation for such software.
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
*/
#include <u.h>
#include <libc.h>
#include "fmt.h"
#include "fmtdef.h"
extern int (*doquote)(int);
/*
* 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, int nin, int nout, Quoteinfo *q, int sharp)
{
int c;
q->quoted = 0;
q->nbytesout = 0;
q->nrunesout = 0;
q->nbytesin = 0;
q->nrunesin = 0;
if(sharp || nin==0 || *s=='\0'){
if(nout < 2)
return;
q->quoted = 1;
q->nbytesout = 2;
q->nrunesout = 2;
}
for(; nin!=0; nin-=1){
c = *s;
if(c == '\0')
break;
if(q->nrunesout+1 > nout)
break;
if((c <= L' ') || (c == L'\'') || (doquote!=nil && doquote(c))){
if(!q->quoted){
if(1+q->nrunesout+1+1 > nout) /* no room for quotes */
break;
q->nrunesout += 2; /* include quotes */
q->nbytesout += 2; /* include quotes */
q->quoted = 1;
}
if(c == '\'') {
q->nbytesout++;
q->nrunesout++; /* quotes reproduce as two characters */
}
}
/* advance input */
s++;
q->nbytesin++;
q->nrunesin++;
/* advance output */
q->nbytesout++;
q->nrunesout++;
}
}
static int
qstrfmt(char *sin, Quoteinfo *q, Fmt *f)
{
int r;
char *t, *s, *m, *me;
ulong fl;
int nc, w;
m = sin;
me = m + q->nbytesin;
w = f->width;
fl = f->flags;
if(!(fl & FmtLeft) && __fmtpad(f, w - q->nbytesout) < 0)
return -1;
t = f->to;
s = f->stop;
FMTCHAR(f, t, s, '\'');
for(nc = q->nrunesin; nc > 0; nc--){
r = *(uchar*)m++;
FMTCHAR(f, t, s, r);
if(r == '\'')
FMTCHAR(f, t, s, r);
}
FMTCHAR(f, t, 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 outlen;
char *s;
Quoteinfo q;
f->flags &= ~FmtPrec; /* ignored for %q %Q, so disable for %s %S in easy case */
s = va_arg(f->args, char *);
if(!s)
return __fmtcpy(f, "<nil>", 5, 5);
if(f->flush)
outlen = 0x7FFFFFFF; /* if we can flush, no output limit */
else
outlen = (char*)f->stop - (char*)f->to;
__quotesetup(s, -1, outlen, &q, f->flags&FmtSharp);
if(!q.quoted)
return __fmtcpy(f, s, q.nrunesin, q.nbytesin);
return qstrfmt(s, &q, f);
}
int
quotestrfmt(Fmt *f)
{
return __quotestrfmt(0, f);
}
void
quotefmtinstall(void)
{
fmtinstall('q', quotestrfmt);
}
int
__needsquotes(char *s, int *quotelenp)
{
Quoteinfo q;
__quotesetup(s, -1, 0x7FFFFFFF, &q, 0);
*quotelenp = q.nbytesout;
return q.quoted;
}