fmtesc.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at https://graphviz.org
  9. *************************************************************************/
  10. /*
  11. * Glenn Fowler
  12. * AT&T Research
  13. *
  14. * return string with expanded escape chars
  15. */
  16. #include <ast/ast.h>
  17. #include <cgraph/gv_ctype.h>
  18. #include <stdbool.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <util/agxbuf.h>
  22. /// quote string as with qb...qe
  23. char *fmtquote(const char *as, const char *qb, const char *qe) {
  24. const size_t n = strlen(as);
  25. const unsigned char *s = (const unsigned char *) as;
  26. const unsigned char *e = s + n;
  27. bool escaped = false;
  28. bool spaced = false;
  29. bool shell = false;
  30. agxbuf b = {0};
  31. if (qb) {
  32. if (qb[0] == '$' && qb[1] == '\'' && qb[2] == 0)
  33. shell = true;
  34. agxbput(&b, qb);
  35. }
  36. char last = '\0';
  37. #define PUT(ch) \
  38. do { \
  39. last = (ch); \
  40. agxbputc(&b, (ch)); \
  41. } while (0)
  42. while (s < e) {
  43. int c = *s++;
  44. if (gv_iscntrl(c) || !gv_isprint(c) || c == '\\') {
  45. escaped = true;
  46. PUT('\\');
  47. switch (c) {
  48. case CC_bel:
  49. c = 'a';
  50. break;
  51. case '\b':
  52. c = 'b';
  53. break;
  54. case '\f':
  55. c = 'f';
  56. break;
  57. case '\n':
  58. c = 'n';
  59. break;
  60. case '\r':
  61. c = 'r';
  62. break;
  63. case '\t':
  64. c = 't';
  65. break;
  66. case CC_vt:
  67. c = 'v';
  68. break;
  69. case CC_esc:
  70. c = 'E';
  71. break;
  72. case '\\':
  73. break;
  74. default:
  75. PUT((char)('0' + ((c >> 6) & 07)));
  76. PUT((char)('0' + ((c >> 3) & 07)));
  77. c = '0' + (c & 07);
  78. break;
  79. }
  80. } else if (qe && strchr(qe, c)) {
  81. escaped = true;
  82. PUT('\\');
  83. } else if (!spaced &&
  84. !escaped &&
  85. (gv_isspace(c) ||
  86. (shell &&
  87. (strchr("\";~&|()<>[]*?", c) ||
  88. (c == '#' && (last == '\0' || gv_isspace(last))
  89. )
  90. )
  91. )
  92. )
  93. )
  94. spaced = true;
  95. PUT((char)c);
  96. }
  97. #undef PUT
  98. if (qb) {
  99. if (!escaped) {
  100. const size_t move_by = (size_t)(shell + !spaced);
  101. if (move_by > 0) {
  102. char *content = agxbdisown(&b);
  103. agxbput(&b, content + move_by);
  104. free(content);
  105. }
  106. }
  107. if (qe && (escaped || spaced))
  108. agxbput(&b, qe);
  109. }
  110. return agxbdisown(&b);
  111. }
  112. /*
  113. * escape the usual suspects and quote chars in qs
  114. */
  115. char *fmtesq(const char *as, const char *qs)
  116. {
  117. return fmtquote(as, NULL, qs);
  118. }
  119. /*
  120. * escape the usual suspects
  121. */
  122. char *fmtesc(const char *as)
  123. {
  124. return fmtquote(as, NULL, NULL);
  125. }