ngspice/src/frontend/postsc.c

726 lines
25 KiB
C

/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1988 Jeffrey M. Hsu
**********/
/*
Postscript driver
*/
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/graph.h"
#include "ngspice/ftedbgra.h"
#include "ngspice/ftedev.h"
#include "ngspice/fteinput.h"
#include "ngspice/fteext.h"
#include "postsc.h"
#include "variable.h"
#include "plotting/graphdb.h"
#define RAD_TO_DEG (180.0 / M_PI)
#define DEVDEP(g) (*((PSdevdep *) (g)->devdep))
#define MAX_PS_LINES 1000
#define SOLID 0
#define DOTTED 1
#define gtype graph->grid.gridtype
#define xoff dispdev->minx
#define yoff dispdev->miny
#define XOFF 48 /* printer left margin */
#define YOFF 48 /* printer bottom margin */
#define XTADJ 0 /* printer text adjustment x */
#define YTADJ 4 /* printer text adjustment y */
#define GRIDSIZE 420 /* printer gridsize divisible by 10, [7-2] */
#define GRIDSIZES 360 /* printer gridsize divisible by [10-8], [6-2] */
#define FONTSIZE 10 /* printer default fontsize */
#define FONTWIDTH 6 /* printer default fontwidth */
#define FONTHEIGHT 14 /* printer default fontheight */
typedef struct {
int red, green, blue;
} COLOR;
/* duplicated colors from src/frontend/plotting/x11.c in rgb-style */
static const COLOR colors[] = {{ 0, 0, 0}, /*0: black */
{255, 255, 255}, /*1: white */
{255, 0, 0}, /*2: red */
{ 0, 0, 255}, /*3: blue */
{255, 165, 0}, /*4: orange */
{ 0, 255, 0}, /*5: green */
{255, 192, 203}, /*6: pink */
{165, 42, 42}, /*7: brown */
{240, 230, 140}, /*8: khaki */
{221, 160, 221}, /*9: plum */
{218, 112, 214}, /*10: orchid */
{238, 130, 238}, /*11: violet */
{176, 48, 96}, /*12: maroon */
{ 64, 224, 208}, /*13: turqoise */
{160, 82, 45}, /*14: sienna */
{255, 127, 80}, /*15: coral */
{ 0, 255, 255}, /*16: cyan */
{255, 0, 255}, /*17: magenta */
{255, 215, 0}, /*18: gold */
{255, 255, 0}, /*19: yello */
{ 96, 96, 96}, /*20: gray for smith grid */
{150, 150, 150}, /*21: gray for smith grid */
{128, 128, 128}}; /*22: gray for normal grid */
typedef struct {
int lastlinestyle, lastcolor; /* initial invalid value */
int lastx, lasty, linecount;
} PSdevdep;
static char *linestyle[] = {
"[]", /* solid */
"[1 2]", /* dotted */
"[7 7]", /* longdashed */
"[3 3]", /* shortdashed */
"[7 2 2 2]", /* longdotdashed */
"[3 2 1 2]", /* shortdotdashed */
"[8 3 2 3]",
"[14 2]",
"[3 5 1 5]" /* dotdashed */
};
static FILE *plotfile;
char psfont[128], psfontsize[32], psscale[32], pscolor[32];
static int fontsize = FONTSIZE;
static int fontwidth = FONTWIDTH;
static int fontheight = FONTHEIGHT;
static int screenflag = 0;
static int colorflag = 0;
static int setbgcolor = 0;
static int settxcolor = 1;
static double scale; /* Used for fine tuning */
static int xtadj; /* text adjustment x */
static int ytadj; /* text adjustment y */
static int hcopygraphid;
static double linewidth;
static double gridlinewidth;
static int maxcolor = 2;
void PS_LinestyleColor(int linestyleid, int colorid);
void PS_SelectColor(int colorid);
void PS_Stroke(void);
static size_t utf8_to_latin9(char * const output, const char *const input,
const size_t length);
/* Set scale, color and size of the plot */
int PS_Init(void)
{
char pswidth[30], psheight[30];
maxcolor = NUMELEMS(colors);
if (!cp_getvar("hcopyscale", CP_STRING, psscale, sizeof(psscale))) {
scale = 1.0;
}
else if (sscanf(psscale, "%lf", &scale) != 1) {
(void) fprintf(cp_err, "Error getting scale value\n");
scale = 1.0;
}
else if ((scale <= 0.0) || (scale > 10.0)) {
(void) fprintf(cp_err, "Scale value %lf is out of range\n",
scale);
scale = 1.0;
}
dispdev->numlinestyles = NUMELEMS(linestyle);
/* plot color */
if (!cp_getvar("hcopypscolor", CP_NUM, &setbgcolor, 0)) {
/* if not set, set plot to b&w and use line styles */
colorflag = 0;
dispdev->numcolors = 2;
} else {
/* get text color and set plot to color */
colorflag = 1;
dispdev->numcolors = maxcolor;
cp_getvar("hcopypstxcolor", CP_NUM, &settxcolor, 0);
}
if (settxcolor > maxcolor || settxcolor < 0) {
fprintf(stderr, "Bad PS text color selection %d\n", settxcolor);
fprintf(stderr, " Maximum for hcopypstxcolor is %d\n\n", maxcolor - 1);
colorflag = 0;
dispdev->numcolors = 2;
}
if (setbgcolor > maxcolor || setbgcolor < 0) {
fprintf(stderr, "Bad PS background color selection %d\n", setbgcolor);
fprintf(stderr, " Maximum for hcopypscolor is %d\n", maxcolor - 1);
fprintf(stderr, " Set to 1 (white)\n\n");
setbgcolor = 1;
}
/* plot size */
if (!cp_getvar("hcopywidth", CP_STRING, pswidth, sizeof( pswidth))) {
dispdev->width = (int)(7.75 * 72.0 * scale); /* (8 1/2 - 3/4) * 72 */
}
else {
sscanf(pswidth, "%d", &(dispdev->width));
if (dispdev->width <= 100)
dispdev->width = 100;
if (dispdev->width >= 10000)
dispdev->width = 10000;
}
if (!cp_getvar("hcopyheight", CP_STRING, psheight, sizeof(psheight))) {
dispdev->height = dispdev->width;
}
else {
sscanf(psheight, "%d", &(dispdev->height));
if (dispdev->height <= 100)
dispdev->height = 100;
if (dispdev->height >= 10000)
dispdev->height = 10000;
}
/* get linewidth information from spinit */
if (!cp_getvar("xbrushwidth", CP_REAL, &linewidth, 0))
linewidth = 0;
if (linewidth < 0)
linewidth = 0;
/* get linewidth for grid from spinit */
if (!cp_getvar("xgridwidth", CP_REAL, &gridlinewidth, 0))
gridlinewidth = linewidth;
if (gridlinewidth < 0)
gridlinewidth = 0;
/* The following side effects have to be considered
* when the printer is called by com_hardcopy !
* gr_init:
* viewportxoff = 8 * fontwidth
* viewportyoff = 4 * fontheight
* gr_resize_internal:
* viewport.width = absolute.width - 2 * viewportxoff
* viewport.height = absolute.height - 2 * viewportyoff
*/
if (!cp_getvar("hcopyfont", CP_STRING, psfont, sizeof(psfont))) {
strcpy(psfont, "Helvetica");
}
if (!cp_getvar("hcopyfontsize", CP_STRING, psfontsize, sizeof(psfontsize))) {
fontsize = 10;
fontwidth = 6;
fontheight = 14;
xtadj = (int)(XTADJ * scale);
ytadj = (int)(YTADJ * scale);
}
else {
sscanf(psfontsize, "%d", &fontsize);
if ((fontsize < 10) || (fontsize > 18))
fontsize = 10;
fontwidth = (int)(0.5 + 0.6 * fontsize);
fontheight = (int)(2.5 + 1.2 * fontsize);
xtadj = (int)(XTADJ * scale * fontsize / 10);
ytadj = (int)(YTADJ * scale * fontsize / 10);
}
screenflag = 0;
dispdev->minx = (int)(XOFF / scale);
dispdev->miny = (int)(YOFF / scale);
return 0;
} /* end of function PS_Init */
/* Plot and fill bounding box */
int PS_NewViewport(GRAPH *graph)
{
int x1, x2, y1, y2;
hcopygraphid = graph->graphid;
/* devdep initially contains name of output file */
if ((plotfile = fopen((char*)graph->devdep, "w")) == NULL) {
perror((char *) graph->devdep);
free(graph->devdep);
graph->devdep = NULL;
graph->n_byte_devdep = 0;
return 1;
}
if (graph->absolute.width) {
/* hardcopying from the screen */
screenflag = 1;
}
/* reasonable values, used in gr_ for placement */
graph->fontwidth = (int)(fontwidth * scale); /* was 12, p.w.h. */
graph->fontheight = (int)(fontheight * scale); /* was 24, p.w.h. */
graph->absolute.width = dispdev->width;
graph->absolute.height = dispdev->height;
/* Also done in gr_init, if called . . . */
graph->viewportxoff = 8 * fontwidth;
graph->viewportyoff = 4 * fontheight;
xoff = (int)(scale * XOFF);
yoff = (int)(scale * YOFF);
x1 = (int) (0.5 * 72 - fontheight);
y1 = x1;
x2 = (int)(graph->absolute.width + .5 * 72);
y2 = (int)(graph->absolute.height + .75 * 72);
/* start file off with a % */
fprintf(plotfile, "%%!PS-Adobe-3.0 EPSF-3.0\n");
fprintf(plotfile, "%%%%Creator: ngspice\n");
fprintf(plotfile, "%%%%BoundingBox: %d %d %d %d\n", x1, y1, x2, y2);
/* Re-encoding to allow 'extended asccii'
* thanks to http://apps.jcns.fz-juelich.de/doku/sc/ps-latin/ */
fprintf(plotfile, "/Re-encode { %% inFont outFont encoding | -\n");
fprintf(plotfile, " /MyEncoding exch def\n");
fprintf(plotfile, " exch findfont\n");
fprintf(plotfile, " dup length dict\n");
fprintf(plotfile, " begin\n");
fprintf(plotfile, " {def} forall\n");
fprintf(plotfile, " /Encoding MyEncoding def\n");
fprintf(plotfile, " currentdict\n");
fprintf(plotfile, " end\n");
fprintf(plotfile, " definefont\n");
fprintf(plotfile, "} def\n");
fprintf(plotfile, "/%s /%sLatin1 ISOLatin1Encoding Re-encode\n", psfont, psfont);
fprintf(plotfile, "%g %g scale\n", 1.0 / scale, 1.0 / scale);
if (colorflag == 1) {
/* set the background to color given in spinit (or 0) */
PS_SelectColor(setbgcolor);
fprintf(plotfile, "%s setrgbcolor\n", pscolor);
fprintf(plotfile, "newpath\n");
fprintf(plotfile, "%d %d moveto %d %d lineto\n", x1, y1, x2, y1);
fprintf(plotfile, "%d %d lineto %d %d lineto\n", x2, y2, x1, y2);
fprintf(plotfile, "closepath fill\n");
}
/* set up a reasonable font */
fprintf(plotfile, "/%sLatin1 findfont %d scalefont setfont\n\n",
psfont, (int) (fontsize * scale));
graph->devdep = TMALLOC(PSdevdep, 1);
graph->n_byte_devdep = sizeof(PSdevdep);
DEVDEP(graph).lastlinestyle = -1;
DEVDEP(graph).lastcolor = -1;
DEVDEP(graph).lastx = -1;
DEVDEP(graph).lasty = -1;
DEVDEP(graph).linecount = 0;
PS_SelectColor(0);
graph->linestyle = -1;
return 0;
}
int PS_Close(void)
{
/* in case PS_Close is called as part of an abort,
w/o having reached PS_NewViewport */
if (plotfile) {
PS_Stroke();
fprintf(plotfile, "showpage\n%%%%EOF\n");
fclose(plotfile);
plotfile = NULL;
}
/* In case of hardcopy command destroy the hardcopy graph
* and reset currentgraph to graphid 1, if possible
*/
if (!screenflag) {
if (hcopygraphid > 0)
DestroyGraph(hcopygraphid);
currentgraph = FindGraph(1);
}
return 0;
}
int PS_Clear(void)
{
/* do nothing */
return 0;
}
int
PS_DrawLine(int x1, int y1, int x2, int y2, bool isgrid)
{
/* note: this is not extendible to more than one graph
=> will have to give NewViewport a writeable graph XXX */
if (DEVDEP(currentgraph).linecount > MAX_PS_LINES ||
DEVDEP(currentgraph).linecount == 0 ||
x1 != DEVDEP(currentgraph).lastx ||
y1 != DEVDEP(currentgraph).lasty)
{
PS_Stroke();
fprintf(plotfile, "newpath\n");
fprintf(plotfile, "%d %d moveto\n", x1 + xoff, y1 + yoff);
DEVDEP(currentgraph).linecount += 1;
}
if (x1 != x2 || y1 != y2) {
fprintf(plotfile, "%d %d lineto\n", x2 + xoff, y2 + yoff);
DEVDEP(currentgraph).linecount += 1;
if(isgrid)
fprintf(plotfile, "%f setlinewidth\n", gridlinewidth);
else
fprintf(plotfile, "%f setlinewidth\n", linewidth);
}
DEVDEP(currentgraph).lastx = x2;
DEVDEP(currentgraph).lasty = y2;
return 0;
}
int PS_Arc(int x0, int y0, int r, double theta, double delta_theta)
{
double x1, y1;
double angle1, angle2;
PS_Stroke();
angle1 = (double) (RAD_TO_DEG * theta);
angle2 = (double) (RAD_TO_DEG * (theta + delta_theta));
x1 = (double) x0 + r * cos(theta);
y1 = (double) y0 + r * sin(theta);
fprintf(plotfile, "%f %f moveto ", x1+(double)xoff, y1+(double)yoff);
fprintf(plotfile, "%d %d %d %f %f arc\n", x0+xoff, y0+yoff, r,
angle1, angle2);
fprintf(plotfile, "stroke\n");
DEVDEP(currentgraph).linecount = 0;
return 0;
}
int PS_Text(const char *text_in, int x, int y, int angle)
{
int savedlstyle, savedcolor;
char *text;
#ifdef EXT_ASC
text = text_in;
#else
{
const size_t n_char_text = strlen(text_in);
text = TMALLOC(char, n_char_text);
utf8_to_latin9(text, text_in, n_char_text);
}
#endif
/* set linestyle to solid
or may get funny color text on some plotters */
savedlstyle = currentgraph->linestyle;
savedcolor = currentgraph->currentcolor;
PS_SetLinestyle(SOLID);
/* set text color to black if background is not white */
if (setbgcolor > 0)
PS_SetColor(0);
else
PS_SetColor(1);
/* if color is given by set hcopytxpscolor=settxcolor, give it a try */
if (settxcolor >= 0)
PS_SetColor(settxcolor);
/* stroke the path if there's an open one */
PS_Stroke();
/* move to (x, y) */
fprintf(plotfile, "%d %d moveto\n", x + xoff + xtadj, y + yoff + ytadj);
/* rotate the text counterclockwise by 'angle' degrees */
fprintf(plotfile, "%d rotate\n", angle);
fprintf(plotfile, "(%s) show\n", text);
/* rotate the text back clockwise by 'angle' degrees */
fprintf(plotfile, "-%d rotate\n", angle);
DEVDEP(currentgraph).lastx = -1;
DEVDEP(currentgraph).lasty = -1;
/* restore old linestyle */
PS_SetColor(savedcolor);
PS_SetLinestyle(savedlstyle);
#ifndef EXT_ASC
txfree(text);
#endif
return 0;
}
/* PS_DefineColor */
/* PS_DefineLinestyle */
int PS_SetLinestyle(int linestyleid)
{
/* special case
get it when PS_Text restores a -1 linestyle */
if (linestyleid == -1) {
currentgraph->linestyle = -1;
return 0;
}
if (linestyleid < 0 || linestyleid > dispdev->numlinestyles) {
internalerror("bad linestyleid inside PS_SetLinestyle");
fprintf(cp_err, "linestyleid is: %d\n", linestyleid);
return 0;
}
PS_LinestyleColor(linestyleid, currentgraph->currentcolor);
return 0;
}
int PS_SetColor(int colorid)
{
PS_LinestyleColor(currentgraph->linestyle, colorid);
return 0;
}
int PS_Update(void)
{
fflush(plotfile);
return 0;
}
/**************** PRIVAT FUNCTIONS OF PS FRONTEND *****************************/
void PS_SelectColor(int colorid) /* should be replaced by PS_DefineColor */
{
char colorN[30] = "", colorstring[30] = "";
char rgb[30], s_red[30] = "0x", s_green[30] = "0x", s_blue[30] = "0x";
int red = 0, green = 0, blue = 0, maxval = 1;
int i;
/* Extract the rgbcolor, format is: "rgb:<red>/<green>/<blue>" */
sprintf(colorN, "color%d", colorid);
if (cp_getvar(colorN, CP_STRING, colorstring, sizeof(colorstring))) {
for (i = 0; colorstring[i]; i++)
if (colorstring[i] == '/' || colorstring[i] == ':')
colorstring[i] = ' ';
sscanf(colorstring, "%s %s %s %s", rgb, &(s_red[2]), &(s_green[2]), &(s_blue[2]));
if ((strlen(s_blue) == strlen(s_red) && strlen(s_green) == strlen(s_red))
&& (strlen(s_blue) > 2) && (strlen(s_blue) < 7)) {
sscanf(s_red, "%x", &red);
sscanf(s_green, "%x", &green);
sscanf(s_blue, "%x", &blue);
maxval = (1 << (strlen(s_blue) - 2) * 4) - 1;
sprintf(colorstring, "%1.3f %1.3f %1.3f",
(double) red/maxval, (double) green/maxval, (double) blue/maxval);
strcpy(pscolor, colorstring);
}
}
if (colorid < 0 || colorid >= maxcolor) {
internalerror("bad colorid inside PS_SelectColor");
} else if (maxval == 1) { /* colorN is not an rgbstring, use default color */
sprintf(colorstring, "%1.3f %1.3f %1.3f", colors[colorid].red/255.0,
colors[colorid].green/255.0, colors[colorid].blue/255.0);
strcpy(pscolor, colorstring);
}
}
void PS_LinestyleColor(int linestyleid, int colorid)
{
/* we have some different linestyles and colors:
- color and linestyle we got via function call
- color and linestyle we used last time for drawing
- generated color and linestyle we'll use for drawing this time */
/* these are the rules:
DOTTED and colored ps -> color20 (used for grid) and SOLID
color18 or 19 and black-white -> linestyle is DOTTED */
int gencolor = 0, genstyle = 0;
if (colorflag == 1) {
genstyle = SOLID;
if (linestyleid == DOTTED)
gencolor = 20;
else
gencolor = colorid;
} else { /* colorflag == 0 -> mono*/
if ((colorid == 18) || (colorid == 19))
genstyle = DOTTED;
else if (linestyleid == -1)
genstyle = 0;
else
genstyle = linestyleid;
}
/* change color if nessecary */
if (colorflag == 1 && gencolor != DEVDEP(currentgraph).lastcolor) {
/* if background is white, set all white line colors to black */
if ((setbgcolor == 1) && (gencolor == 1))
PS_SelectColor(0);
else
PS_SelectColor(gencolor);
PS_Stroke();
fprintf(plotfile, "%s setrgbcolor\n", pscolor);
DEVDEP(currentgraph).lastcolor = gencolor;
}
currentgraph->currentcolor = colorid;
/* change linestyle if nessecary */
if (colorflag == 0 && genstyle != DEVDEP(currentgraph).lastlinestyle) {
PS_Stroke();
fprintf(plotfile, "%s 0 setdash\n", linestyle[genstyle]);
DEVDEP(currentgraph).lastlinestyle = genstyle;
}
currentgraph->linestyle = linestyleid;
}
void PS_Stroke(void)
{
/* strokes an open path */
if (DEVDEP(currentgraph).linecount > 0) {
fprintf(plotfile, "stroke\n");
DEVDEP(currentgraph).linecount = 0;
}
}
/* UTF-8 to ISO-8859-1/ISO-8859-15 mapper.
* Return 0..255 for valid ISO-8859-15 code points, 256 otherwise.
*/
static inline unsigned int to_latin9(const unsigned int code)
{
/* Code points 0 to U+00FF are the same in both. */
if (code < 256U)
return code;
switch (code) {
case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */
case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */
case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */
case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */
case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */
case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */
case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */
case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */
default: return 256U;
}
}
/* Convert an UTF-8 string to ISO-8859-15.
* All invalid sequences are ignored.
* Note: output == input is allowed,
* but input < output < input + length
* is not.
* Output has to have room for (length+1) chars, including the trailing NUL byte.
from http://stackoverflow.com/questions/11156473/is-there-a-way-to-convert-from-utf8-to-iso-8859-1#11173493
*/
size_t utf8_to_latin9(char * const output, const char *const input,
const size_t length)
{
unsigned char *out = (unsigned char *)output;
const unsigned char *in = (const unsigned char *)input;
const unsigned char *const end = (const unsigned char *)input + length;
unsigned int c;
while (in < end)
if (*in < 128)
*(out++) = *(in++); /* Valid codepoint */
else
if (*in < 192)
in++; /* 10000000 .. 10111111 are invalid */
else
if (*in < 224) { /* 110xxxxx 10xxxxxx */
if (in + 1 >= end)
break;
if ((in[1] & 192U) == 128U) {
c = to_latin9((((unsigned int)(in[0] & 0x1FU)) << 6U)
| ((unsigned int)(in[1] & 0x3FU)));
if (c < 256)
*(out++) = (unsigned char)c;
}
in += 2;
}
else
if (*in < 240) { /* 1110xxxx 10xxxxxx 10xxxxxx */
if (in + 2 >= end)
break;
if ((in[1] & 192U) == 128U &&
(in[2] & 192U) == 128U) {
c = to_latin9((((unsigned int)(in[0] & 0x0FU)) << 12U)
| (((unsigned int)(in[1] & 0x3FU)) << 6U)
| ((unsigned int)(in[2] & 0x3FU)));
if (c < 256)
*(out++) = (unsigned char)c;
}
in += 3;
}
else
if (*in < 248) { /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
if (in + 3 >= end)
break;
if ((in[1] & 192U) == 128U &&
(in[2] & 192U) == 128U &&
(in[3] & 192U) == 128U) {
c = to_latin9((((unsigned int)(in[0] & 0x07U)) << 18U)
| (((unsigned int)(in[1] & 0x3FU)) << 12U)
| (((unsigned int)(in[2] & 0x3FU)) << 6U)
| ((unsigned int)(in[3] & 0x3FU)));
if (c < 256)
*(out++) = (unsigned char)c;
}
in += 4;
}
else
if (*in < 252) { /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
if (in + 4 >= end)
break;
if ((in[1] & 192U) == 128U &&
(in[2] & 192U) == 128U &&
(in[3] & 192U) == 128U &&
(in[4] & 192U) == 128U) {
c = to_latin9((((unsigned int)(in[0] & 0x03U)) << 24U)
| (((unsigned int)(in[1] & 0x3FU)) << 18U)
| (((unsigned int)(in[2] & 0x3FU)) << 12U)
| (((unsigned int)(in[3] & 0x3FU)) << 6U)
| ((unsigned int)(in[4] & 0x3FU)));
if (c < 256)
*(out++) = (unsigned char)c;
}
in += 5;
}
else
if (*in < 254) { /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
if (in + 5 >= end)
break;
if ((in[1] & 192U) == 128U &&
(in[2] & 192U) == 128U &&
(in[3] & 192U) == 128U &&
(in[4] & 192U) == 128U &&
(in[5] & 192U) == 128U) {
c = to_latin9((((unsigned int)(in[0] & 0x01U)) << 30U)
| (((unsigned int)(in[1] & 0x3FU)) << 24U)
| (((unsigned int)(in[2] & 0x3FU)) << 18U)
| (((unsigned int)(in[3] & 0x3FU)) << 12U)
| (((unsigned int)(in[4] & 0x3FU)) << 6U)
| ((unsigned int)(in[5] & 0x3FU)));
if (c < 256)
*(out++) = (unsigned char)c;
}
in += 6;
}
else
in++; /* 11111110 and 11111111 are invalid */
/* Terminate the output string. */
*out = '\0';
return (size_t)(out - (unsigned char *)output);
}