emitf.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. /** --- BEGIN COPYRIGHT BLOCK ---
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. --- END COPYRIGHT BLOCK --- */
  38. #include <stdarg.h> /* va_list etc. */
  39. #include <stdio.h> /* sprintf */
  40. #include <stdlib.h> /* malloc, realloc, free */
  41. #include <string.h> /* strchr, strpbrk etc. */
  42. #include "dsgw.h" /* dsgw_ch_malloc, dsgw_ch_strdup */
  43. typedef void* (*dsgw_producer) (void*, const char*, size_t);
  44. static size_t
  45. produce_fill (dsgw_producer produce, void** parm,
  46. size_t fill, unsigned zero)
  47. {
  48. static const char* zeroes = "00000000";
  49. static const char* blanks = " ";
  50. size_t result = 0;
  51. while (fill > 0) {
  52. long n = fill;
  53. if (n > 8) n = 8;
  54. if (zero) {
  55. *parm = produce (*parm, zeroes, n);
  56. } else {
  57. *parm = produce (*parm, blanks, n);
  58. }
  59. if (*parm == NULL) return result;
  60. result += n;
  61. fill -= n;
  62. }
  63. return result;
  64. }
  65. #define FLAG_LEFT 1 /* align left */
  66. #define FLAG_ZERO 2 /* zero fill */
  67. #define FLAG_CONST 4
  68. static size_t
  69. produce_string (dsgw_producer produce, void** parm,
  70. const char* str, unsigned flags, int width, int precision)
  71. {
  72. size_t fill;
  73. size_t bytes;
  74. size_t result = 0;
  75. if (*parm == NULL) return result;
  76. if (width < 0) {
  77. width = - width;
  78. flags ^= FLAG_LEFT;
  79. }
  80. if (width == 0 && precision < 0) {
  81. fill = 0;
  82. bytes = strlen (str);
  83. } else {
  84. char* s = (char*)str; /* cast away const (for LDAP_UTF8INC) */
  85. size_t chars = 0;
  86. while (*s && ((precision < 0) || (chars < precision))) {
  87. LDAP_UTF8INC(s);
  88. ++chars;
  89. }
  90. fill = (width > chars) ? (width - chars) : 0;
  91. bytes = (s - str);
  92. }
  93. if (fill && ! (flags & FLAG_LEFT)) {
  94. result += produce_fill (produce, parm, fill, flags & FLAG_ZERO);
  95. }
  96. if (bytes) {
  97. *parm = produce (*parm, str, bytes);
  98. if (*parm == NULL) return result;
  99. result += bytes;
  100. }
  101. if (fill && (flags & FLAG_LEFT)) {
  102. result += produce_fill (produce, parm, fill, flags & FLAG_ZERO);
  103. }
  104. return result;
  105. }
  106. static const char* type_chars = "%dioxXueEgGfcsp";
  107. static size_t
  108. count_slots (const char* s)
  109. {
  110. size_t n = 0;
  111. while ((s = strchr (s, '%')) != NULL) {
  112. const char* l = strpbrk (s+1, type_chars);
  113. const char* c;
  114. if (l == NULL) {
  115. n += 3;
  116. break;
  117. }
  118. ++n;
  119. for (c = s+1; c != l; ++c) {
  120. if (*c == '*') ++n;
  121. }
  122. s = *l ? l+1 : l;
  123. }
  124. return n;
  125. }
  126. typedef struct {
  127. char type;
  128. #define TYPE_I 0
  129. #define TYPE_U 1
  130. #define TYPE_F 2
  131. #define TYPE_LI 3
  132. #define TYPE_LU 4
  133. #define TYPE_LF 5
  134. #define TYPE_S 6
  135. #define TYPE_P 7
  136. #define TYPE_PERCENT 8 /* e.g. %% */
  137. #define TYPE_WIDTH 9
  138. #define TYPE_PRECISION 10
  139. unsigned char flags;
  140. int arg; /* An index into an array of dsgw_arg_t,
  141. or (if flags & FLAG_CONST) the width or precision value. */
  142. } dsgw_slot_t;
  143. typedef union {
  144. int i;
  145. unsigned int u;
  146. double f;
  147. long li;
  148. unsigned long lu;
  149. long double lf;
  150. const char* s;
  151. void* p;
  152. } dsgw_arg_t;
  153. #define DEFSLOTC 8 /* A format string rarely contains more slots. */
  154. #define DEFFMTC 16 /* A single format rarely contains more chars. */
  155. static size_t
  156. dsgw_vxprintf (dsgw_producer produce, void* parm,
  157. const char* format, va_list argl)
  158. /* This function works like vsprintf(), except it:
  159. - supports parameter reordering, using %posp$.
  160. - is UTF8-aware.
  161. - delivers output by calling the function 'produce'.
  162. - returns the total number of bytes produced.
  163. This function interprets all string parameters as UTF8.
  164. */
  165. {
  166. size_t result = 0; /* total number of bytes produced */
  167. /* Each place that 'format' refers to an argument is called a 'slot'. */
  168. dsgw_slot_t defslot[DEFSLOTC];
  169. dsgw_slot_t* slot = defslot; /* in order of their appearance in format */
  170. dsgw_slot_t* islot = NULL; /* next slot to process */
  171. dsgw_slot_t* aslot = NULL; /* another cursor */
  172. dsgw_arg_t defargv[DEFSLOTC];
  173. dsgw_arg_t* argv = defargv; /* in order of their appearance in argl */
  174. size_t argi = 0; /* index of next argument (in argl/argv) */
  175. char deffmt[DEFFMTC];
  176. char* fmt = deffmt;
  177. size_t fmtc = DEFFMTC;
  178. const char* next;
  179. const char* f;
  180. char buf [1024];
  181. int i;
  182. i = count_slots (format);
  183. /*fprintf (stderr, "slots: %i\n", i);*/
  184. if (i > DEFSLOTC) { /* defslot isn't big enough. */
  185. slot = (dsgw_slot_t*) malloc (i * sizeof(dsgw_slot_t));
  186. }
  187. /* get slot types from format: */
  188. islot = slot;
  189. next = format;
  190. while ((f = strchr (next, '%')) != NULL) {
  191. const char* l = f+1;
  192. unsigned flags = 0;
  193. int number = -1;
  194. char size;
  195. if (*l >= '1' && *l <= '9') {
  196. number = 0;
  197. do { number = (number * 10) + (*l++ - '0');
  198. } while (*l >= '0' && *l <= '9');
  199. }
  200. if (*l == '$') {
  201. ++l;
  202. if (number > 0) {
  203. argi = number - 1;
  204. }
  205. number = -1;
  206. }
  207. if (number >= 0) { /* width */
  208. islot->arg = number;
  209. flags |= FLAG_CONST;
  210. } else {
  211. while (1) { /* flags */
  212. switch (*l) {
  213. case '-': flags |= FLAG_LEFT; ++l; continue;
  214. case '0': flags |= FLAG_ZERO; ++l; continue;
  215. case '+':
  216. case ' ':
  217. case '#': ++l; continue;
  218. default: break;
  219. }
  220. break;
  221. }
  222. if (*l == '*') { /* width */
  223. number = 0;
  224. ++l;
  225. islot->arg = argi++;
  226. } else if (*l >= '1' && *l <= '9') { /* width */
  227. number = 0;
  228. do { number = (number * 10) + (*l++ - '0');
  229. } while (*l >= '0' && *l <= '9');
  230. islot->arg = number;
  231. flags |= FLAG_CONST;
  232. }
  233. }
  234. if (number >= 0) {
  235. islot->type = TYPE_WIDTH;
  236. islot->flags = flags;
  237. flags &= ~ FLAG_CONST;
  238. ++islot;
  239. }
  240. if (*l == '.') {
  241. islot->type = TYPE_PRECISION;
  242. ++l;
  243. if (*l == '*') {
  244. ++l;
  245. islot->arg = argi++;
  246. islot->flags = 0;
  247. } else {
  248. number = 0;
  249. while (*l >= '0' && *l <= '9')
  250. number = (number * 10) + (*l++ - '0');
  251. islot->arg = number;
  252. islot->flags = FLAG_CONST;
  253. }
  254. ++islot;
  255. }
  256. switch (*l) { /* size modifier */
  257. case 'h':
  258. case 'l':
  259. case 'L': size = *l++; break;
  260. default: size = '\0';
  261. }
  262. islot->flags = 0;
  263. switch (*l) { /* type */
  264. case 'd':
  265. case 'i': islot->type = (size == 'l') ? TYPE_LI : TYPE_I; break;
  266. case 'o':
  267. case 'x': case 'X':
  268. case 'u': islot->type = (size == 'l') ? TYPE_LU : TYPE_U; break;
  269. case 'e': case 'E':
  270. case 'g': case 'G':
  271. case 'f': islot->type = (size == 'L') ? TYPE_LF : TYPE_F; break;
  272. case 'c': islot->type = TYPE_I; break;
  273. case 's': islot->type = TYPE_S; break;
  274. case 'p': islot->type = TYPE_P; break;
  275. case '%': islot->type = TYPE_PERCENT;
  276. islot->flags = FLAG_CONST; break;
  277. default: /* unknown type */
  278. goto bail; /* don't produce anything. */
  279. /* It might be more helpful to produce the slots up to
  280. this one, and maybe output this format substring, too.
  281. That way, someone reading the output might get a clue
  282. what went wrong.
  283. */
  284. }
  285. if (islot->type != TYPE_PERCENT) {
  286. islot->arg = argi++;
  287. }
  288. ++islot;
  289. next = *l ? l+1 : l;
  290. }
  291. /* argi = the length of argl/argv: */
  292. argi = 0;
  293. for (aslot = slot; aslot != islot; ++aslot) {
  294. if (argi <= aslot->arg && ! (aslot->flags & FLAG_CONST)) {
  295. argi = aslot->arg + 1;
  296. }
  297. }
  298. if (argi > DEFSLOTC) { /* defargv isn't big enough */
  299. argv = (dsgw_arg_t*) malloc (argi * sizeof(dsgw_arg_t));
  300. }
  301. /* copy arguments from argl to argv: */
  302. /*fprintf (stderr, "slot:type:value:");*/
  303. for (i = 0; i < argi; ++i) {
  304. for (aslot = slot; aslot != islot; ++aslot) {
  305. if ( ! (aslot->flags & FLAG_CONST) && aslot->arg == i) {
  306. break;
  307. }
  308. }
  309. if (aslot == islot) { /* No slot refers to this arg. */
  310. if (va_arg (argl, const char*)); /* Skip over it. */
  311. } else {
  312. /*fprintf (stderr, " %i:%i", (int)(aslot-slot), aslot->type);*/
  313. switch (aslot->type) {
  314. case TYPE_U: argv[i].u = va_arg (argl, unsigned); break;
  315. case TYPE_F: argv[i].f = va_arg (argl, double); break;
  316. case TYPE_LI: argv[i].li = va_arg (argl, long); break;
  317. case TYPE_LU: argv[i].lu = va_arg (argl, unsigned long); break;
  318. case TYPE_LF: argv[i].lf = va_arg (argl, long double); break;
  319. case TYPE_P: argv[i].p = va_arg (argl, void*); break;
  320. case TYPE_S: argv[i].s = va_arg (argl, const char*);
  321. /*fprintf (stderr, ":\"%s\"", argv[i].s);*/
  322. break;
  323. case TYPE_PERCENT: break; /* no arg */
  324. case TYPE_WIDTH:
  325. case TYPE_PRECISION:
  326. case TYPE_I: argv[i].i = va_arg (argl, int);
  327. /*fprintf (stderr, ":%i", argv[i].i);*/
  328. do {
  329. switch (aslot->type) {
  330. case TYPE_WIDTH:
  331. case TYPE_PRECISION:
  332. if ( ! (aslot->flags & FLAG_CONST) && aslot->arg == i) {
  333. aslot->arg = argv[i].i;
  334. aslot->flags |= FLAG_CONST;
  335. }
  336. break;
  337. default: break;
  338. }
  339. } while (++aslot != islot);
  340. break;
  341. }
  342. }
  343. }
  344. /*fprintf (stderr, "\n");*/
  345. /* produce output: */
  346. islot = slot;
  347. next = format;
  348. while (parm && (f = strchr (next, '%'))) {
  349. const char* l = strpbrk (f+1, type_chars);
  350. if (l == NULL) {
  351. break;
  352. }
  353. if (parm && f != next) { /* produce the substring next..f-1 */
  354. const size_t n = (f - next);
  355. parm = produce (parm, next, n);
  356. if (parm) result += n;
  357. }
  358. next = l + 1;
  359. { /* fmt = f..l */
  360. const char* dollar;
  361. const size_t fc = (next - f);
  362. if (fmtc <= fc) {
  363. fmtc = fc + 1;
  364. if (fmt == deffmt) fmt = malloc (fmtc);
  365. else fmt = realloc (fmt, fmtc);
  366. }
  367. memcpy (fmt, f, fc);
  368. fmt[fc] = '\0';
  369. if ((dollar = strchr (fmt, '$')) != NULL) {
  370. /* remove posp$ from the beginning of fmt */
  371. memmove (fmt + 1, dollar + 1, fc - (dollar - fmt));
  372. }
  373. /*fprintf (stderr, "fmt: \"%s\"\n", fmt);*/
  374. }
  375. /* produce a single argument */
  376. switch (islot->type) {
  377. case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break;
  378. case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break;
  379. case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break;
  380. case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break;
  381. case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break;
  382. case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break;
  383. case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break;
  384. case TYPE_WIDTH:
  385. case TYPE_PRECISION:
  386. switch ((++islot)->type) {
  387. case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break;
  388. case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break;
  389. case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break;
  390. case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break;
  391. case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break;
  392. case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break;
  393. case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break;
  394. case TYPE_WIDTH:
  395. case TYPE_PRECISION:
  396. switch ((++islot)->type) {
  397. case TYPE_I: PR_snprintf (buf, 1024, fmt, argv[islot->arg].i); break;
  398. case TYPE_U: PR_snprintf (buf, 1024, fmt, argv[islot->arg].u); break;
  399. case TYPE_F: PR_snprintf (buf, 1024, fmt, argv[islot->arg].f); break;
  400. case TYPE_LI: PR_snprintf (buf, 1024, fmt, argv[islot->arg].li); break;
  401. case TYPE_LU: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lu); break;
  402. case TYPE_LF: PR_snprintf (buf, 1024, fmt, argv[islot->arg].lf); break;
  403. case TYPE_P: PR_snprintf (buf, 1024, fmt, argv[islot->arg].p); break;
  404. case TYPE_WIDTH:
  405. case TYPE_PRECISION: goto bail; /* how did this happen? */
  406. case TYPE_PERCENT:
  407. case TYPE_S: /* with width and precision */
  408. result += produce_string (produce, &parm,
  409. (islot->type == TYPE_S) ? argv[islot->arg].s : "%",
  410. islot[-2].flags, islot[-2].arg, islot[-1].arg);
  411. goto skip_buf;
  412. }
  413. break;
  414. case TYPE_PERCENT:
  415. case TYPE_S: /* with width or precision (not both) */
  416. if (islot[-1].type == TYPE_WIDTH) {
  417. result += produce_string (produce, &parm,
  418. (islot->type == TYPE_S) ? argv[islot->arg].s : "%",
  419. islot[-1].flags, islot[-1].arg, -1);
  420. } else {
  421. result += produce_string (produce, &parm,
  422. (islot->type == TYPE_S) ? argv[islot->arg].s : "%",
  423. 0, 0, islot[-1].arg);
  424. }
  425. goto skip_buf;
  426. }
  427. break;
  428. case TYPE_PERCENT:
  429. case TYPE_S: /* with neither width nor precision */
  430. result += produce_string (produce, &parm,
  431. (islot->type == TYPE_S) ? argv[islot->arg].s : "%",
  432. 0, 0, -1);
  433. goto skip_buf;
  434. }
  435. if (parm && *buf) { /* produce buf */
  436. const size_t n = strlen (buf);
  437. parm = produce (parm, buf, n);
  438. if (parm) result += n;
  439. }
  440. skip_buf:
  441. ++islot;
  442. }
  443. if (parm && *next) { /* produce the remainder of format */
  444. const size_t n = strlen (next);
  445. parm = produce (parm, next, n);
  446. if (parm) result += n;
  447. }
  448. bail:
  449. if (fmt != deffmt) free (fmt);
  450. if (argv != defargv) free (argv);
  451. if (slot != defslot) free (slot);
  452. /*fprintf (stderr, "------\n");*/
  453. return result;
  454. }
  455. size_t
  456. dsgw_fputn (FILE* f, const char* s, size_t n)
  457. {
  458. auto const size_t result =
  459. fwrite (s, sizeof(char), n, f);
  460. dsgw_log_out (s, result);
  461. return result;
  462. }
  463. static const char*
  464. strnbrk (const char* str, size_t n, const char* brk)
  465. {
  466. for (; n > 0; ++str, --n) {
  467. if (strchr (brk, *str)) {
  468. return str;
  469. }
  470. }
  471. return NULL;
  472. }
  473. static int quotation_depth = 0;
  474. static int quotation_type[4]; /* maximum depth */
  475. #define QUOTATION_JAVASCRIPT_ENDOFLINE 1
  476. static size_t
  477. dsgw_emitr (int depth, const char* s, size_t n)
  478. {
  479. static const char* linebreak = "' +\n'";
  480. static const size_t linebreak_len = 5;
  481. auto size_t result = 0;
  482. if (n == 0) {
  483. return 0;
  484. } else if (depth == 0) {
  485. return dsgw_fputn (stdout, s, n);
  486. }
  487. --depth;
  488. switch (quotation_type[depth]) {
  489. case QUOTATION_JAVASCRIPT:
  490. case QUOTATION_JAVASCRIPT_MULTILINE:
  491. case QUOTATION_JAVASCRIPT_ENDOFLINE:
  492. {
  493. auto const char* t;
  494. for (t = s; (t = strnbrk (t, n, "'\\\n")) != NULL; ++t) {
  495. switch (*t) {
  496. case '\n': /* output \n */
  497. if (t != s) {
  498. if (quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) {
  499. dsgw_emitr (depth, linebreak, linebreak_len);
  500. }
  501. result += dsgw_emitr (depth, s, t - s);
  502. }
  503. if (dsgw_emitr (depth, "\\n", 2) > 1) ++result;
  504. if (quotation_type[depth] == QUOTATION_JAVASCRIPT_MULTILINE) {
  505. quotation_type[depth] = QUOTATION_JAVASCRIPT_ENDOFLINE;
  506. }
  507. break;
  508. default: /* insert \ */
  509. if (quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) {
  510. quotation_type[depth] = QUOTATION_JAVASCRIPT_MULTILINE;
  511. dsgw_emitr (depth, linebreak, linebreak_len);
  512. }
  513. result += dsgw_emitr (depth, s, t - s);
  514. dsgw_emitr (depth, "\\", 1);
  515. result += dsgw_emitr (depth, t, 1);
  516. break;
  517. }
  518. n -= (t - s) + 1;
  519. s = t + 1;
  520. }
  521. }
  522. if (n > 0 &&
  523. quotation_type[depth] == QUOTATION_JAVASCRIPT_ENDOFLINE) {
  524. quotation_type[depth] = QUOTATION_JAVASCRIPT_MULTILINE;
  525. dsgw_emitr (depth, linebreak, linebreak_len);
  526. }
  527. break;
  528. default:
  529. break;
  530. }
  531. if (n > 0) {
  532. result += dsgw_emitr (depth, s, n);
  533. }
  534. return result;
  535. }
  536. static size_t
  537. dsgw_emitq (FILE* f, const char* s, size_t n)
  538. {
  539. if (f == stdout && quotation_depth > 0) {
  540. return dsgw_emitr (quotation_depth, s, n);
  541. }
  542. return dsgw_fputn (f, s, n);
  543. }
  544. void
  545. dsgw_quotation_begin (int kind)
  546. {
  547. if (quotation_depth >= 4) exit (4);
  548. switch (kind) {
  549. case QUOTATION_JAVASCRIPT:
  550. case QUOTATION_JAVASCRIPT_MULTILINE:
  551. dsgw_emitq (stdout, "'", 1);
  552. break;
  553. default:
  554. break;
  555. }
  556. quotation_type[quotation_depth++] = kind;
  557. }
  558. void
  559. dsgw_quotation_end()
  560. {
  561. if (quotation_depth > 0) switch (quotation_type[--quotation_depth]) {
  562. case QUOTATION_JAVASCRIPT:
  563. case QUOTATION_JAVASCRIPT_MULTILINE:
  564. case QUOTATION_JAVASCRIPT_ENDOFLINE:
  565. dsgw_emitq (stdout, "'", 1);
  566. break;
  567. default:
  568. break;
  569. }
  570. }
  571. int
  572. dsgw_quote_emits (int kind, const char* s)
  573. {
  574. int result;
  575. dsgw_quotation_begin (kind);
  576. result = dsgw_emits (s);
  577. dsgw_quotation_end();
  578. return result;
  579. }
  580. int
  581. dsgw_quote_emitf (int kind, const char* format, ...)
  582. {
  583. int result;
  584. va_list argl;
  585. va_start (argl, format);
  586. dsgw_quotation_begin (kind);
  587. result = dsgw_emitfv (format, argl);
  588. dsgw_quotation_end();
  589. va_end (argl);
  590. return result;
  591. }
  592. static UConverter* emit_converter = NULL;
  593. /* given string is utf8 - emit_converter converts given string
  594. to some natural language encoding requested by the client */
  595. void*
  596. dsgw_emitn (void* parm, const char* s, size_t n)
  597. {
  598. if (emit_converter == NULL) {
  599. if (dsgw_emitq ((FILE*)parm, s, n) != n) {
  600. return NULL;
  601. }
  602. } else {
  603. #define CONVERT_BUFSIZE 2048
  604. char buf [CONVERT_BUFSIZE]; /* faster than malloc/free */
  605. char *bufptr = buf;
  606. size_t len = 0;
  607. size_t slen = 0;
  608. UErrorCode err = U_ZERO_ERROR;
  609. int result;
  610. do {
  611. bufptr = buf; /* reset to beginning of buf */
  612. s += slen; /* advance pointer to next unconverted chars */
  613. /* convert as many chars from s as will fit in buf */
  614. result = dsgw_convert(DSGW_FROM_UTF8, emit_converter,
  615. &bufptr, CONVERT_BUFSIZE, &len,
  616. s, n, &slen, &err);
  617. /* write the converted chars to the output */
  618. n = dsgw_emitq ((FILE*)parm, buf, len);
  619. } while ((result == 0) && (n == len));
  620. ucnv_reset (emit_converter);
  621. if (n != len) {
  622. return NULL;
  623. }
  624. }
  625. return parm;
  626. }
  627. #if 0
  628. static void
  629. dsgw_convert (void* parm, const char* s, size_t n)
  630. /* Transform the output, in a visually distinctive way.
  631. This function is intended for testing, only.
  632. */
  633. {
  634. while (parm && n > 0) {
  635. const size_t len = LDAP_UTF8LEN(s);
  636. if (len == 1 && *s >= '!' && *s <= '~') { /* ASCII */
  637. /* output the double-width variant of this character */
  638. unsigned c = (unsigned)*s - '!' + 0xFF01;
  639. unsigned char buf[3];
  640. buf[2] = 0x80 | (c & 0x3F); c >>= 6;
  641. buf[1] = 0x80 | (c & 0x3F); c >>= 6;
  642. buf[0] = 0xE0 | (c & 0x0F);
  643. parm = dsgw_emitn (parm, (char*)buf, 3);
  644. } else {
  645. parm = dsgw_emitn (parm, s, len);
  646. }
  647. if (parm) {
  648. n -= len;
  649. s += len;
  650. }
  651. }
  652. }
  653. #endif
  654. int
  655. dsgw_emits (const char* s)
  656. /* This function works like fputs(s, stdout), except it
  657. converts from UTF8 to the client's preferred charset.
  658. */
  659. {
  660. size_t n = strlen (s);
  661. if (n > 0 && dsgw_emitn (stdout, s, n) == NULL) {
  662. return EOF;
  663. }
  664. return n;
  665. }
  666. int
  667. dsgw_emitfv (const char* format, va_list argl)
  668. /* This function works like vprintf(), except it:
  669. - supports parameter reordering, using %posp$.
  670. - is UTF8-aware.
  671. - converts to the client's preferred charset.
  672. This function interprets all string parameters as UTF8.
  673. */
  674. {
  675. return( dsgw_vxprintf (dsgw_emitn, stdout, format, argl));
  676. }
  677. int
  678. dsgw_emitf (const char* format, ...)
  679. {
  680. int rc;
  681. va_list argl;
  682. va_start (argl, format);
  683. rc = dsgw_emitfv (format, argl);
  684. va_end (argl);
  685. return( rc );
  686. }
  687. typedef struct struct_item_t {
  688. char* i_val;
  689. double i_q;
  690. } item_t;
  691. static size_t
  692. list_count (const char* list)
  693. {
  694. const char* s;
  695. size_t n = 1;
  696. if (list == NULL || *list == '\0') return 0;
  697. for (s = list - 1; (s = strchr (s + 1, ',')) != NULL; ++n);
  698. return n;
  699. }
  700. static item_t*
  701. list_parse (char* slist, size_t items)
  702. {
  703. char* s = slist;
  704. item_t* item;
  705. size_t i = 0;
  706. if (items <= 0) return NULL;
  707. item = (item_t*) dsgw_ch_malloc (items * sizeof(item_t));
  708. while (ldap_utf8isspace (s)) LDAP_UTF8INC(s);
  709. while (s && *s) {
  710. if (i >= items) exit (1);
  711. item[i].i_q = 1.0;
  712. item[i++].i_val = s;
  713. if ((s = strchr (s, ',')) != NULL) {
  714. *s = '\0';
  715. while (ldap_utf8isspace (LDAP_UTF8INC(s)));
  716. }
  717. }
  718. if (i != items) exit (1);
  719. for (i = 0; i < items; ++i) {
  720. if ((s = strchr (item[i].i_val, ';')) != NULL) {
  721. *s = '\0';
  722. do {
  723. while (ldap_utf8isspace (LDAP_UTF8INC(s)));
  724. if (*s == 'q' || *s == 'Q') {
  725. while (ldap_utf8isspace (LDAP_UTF8INC(s)));
  726. if (*s == '=') {
  727. item[i].i_q = strtod(++s, &s);
  728. }
  729. }
  730. } while ((s = strchr (s, ';')) != NULL);
  731. }
  732. /* Remove trailing whitespace from item[i].i_val: */
  733. s = item[i].i_val;
  734. s += strlen (s);
  735. while (ldap_utf8isspace (LDAP_UTF8DEC(s)));
  736. s[1] = '\0';
  737. /*printf("%s;q=%.2f\n", item[i].i_val, item[i].i_q);*/
  738. }
  739. return item;
  740. }
  741. static void
  742. list_sort (item_t item[], size_t items)
  743. {
  744. /* This implementation is suboptimal, but adequate. */
  745. int sorted;
  746. size_t i;
  747. do {
  748. sorted = 1;
  749. for (i = 0; i+1 < items ; ++i) {
  750. if (item[i].i_q < item[i+1].i_q) { /* swap i & i+1 */
  751. auto item_t temp;
  752. memcpy (&temp, &item[i], sizeof(item_t));
  753. memcpy (&item[i], &item[i+1], sizeof(item_t));
  754. memcpy (&item[i+1], &temp, sizeof(item_t));
  755. sorted = 0;
  756. }
  757. }
  758. } while ( ! sorted);
  759. }
  760. int
  761. is_UTF_8 (const char* charset)
  762. {
  763. return charset != NULL &&
  764. (!strcasecmp (charset, UNICODE_ENCODING_UTF_8) ||
  765. !strcasecmp (charset, "UNICODE-1-1-UTF-8"));
  766. }
  767. static int
  768. charset_is_supported (char* s)
  769. {
  770. UConverter* converter;
  771. UErrorCode err = U_ZERO_ERROR;
  772. if (is_UTF_8 (s)) {
  773. return 1;
  774. }
  775. converter = ucnv_open (s, &err);
  776. if (err == U_ZERO_ERROR) {
  777. ucnv_close (converter);
  778. return 1;
  779. }
  780. return 0;
  781. }
  782. static char*
  783. choose_charset (char* slist)
  784. /* Return the best charset from the given list. */
  785. {
  786. const size_t items = list_count (slist);
  787. char* sbuf;
  788. item_t* item;
  789. size_t i;
  790. if (items <= 0) return slist;
  791. sbuf = dsgw_ch_strdup (slist);
  792. item = list_parse (sbuf, items);
  793. for (i = 0; i < items; ++i) {
  794. if (is_UTF_8 (item[i].i_val)) {
  795. break; /* choose this one */
  796. }
  797. }
  798. if (i >= items) {
  799. list_sort (item, items);
  800. for (i = 0; i < items; ++i) {
  801. auto char* charset = item[i].i_val;
  802. if (!strcmp ("*", charset)) {
  803. i = items; /* choose UTF_8 */
  804. } else if (charset_is_supported (charset)) {
  805. break; /* choose this one */
  806. }
  807. }
  808. }
  809. if (i >= items) {
  810. strcpy (sbuf, UNICODE_ENCODING_UTF_8);
  811. } else if (sbuf != item[i].i_val) {
  812. memmove (sbuf, item[i].i_val, strlen(item[i].i_val) + 1);
  813. }
  814. free (item);
  815. return sbuf;
  816. }
  817. char*
  818. dsgw_emit_converts_to (char* charset)
  819. {
  820. const char* target;
  821. if (emit_converter != NULL) {
  822. ucnv_close (emit_converter);
  823. emit_converter = NULL;
  824. }
  825. if (charset) charset = choose_charset (charset);
  826. if (charset && *charset) {
  827. target = charset;
  828. } else {
  829. target = ISO_8859_1_ENCODING;
  830. }
  831. if ( ! is_UTF_8 (target)) {
  832. UErrorCode err = U_ZERO_ERROR;
  833. emit_converter = ucnv_open(target, &err);
  834. if (err != U_ZERO_ERROR) {
  835. emit_converter = NULL;
  836. charset = UNICODE_ENCODING_UTF_8;
  837. }
  838. }
  839. return charset;
  840. }