]> git.sesse.net Git - plocate/blob - serializer.cpp
Release plocate 1.1.22.
[plocate] / serializer.cpp
1 #include "serializer.h"
2
3 #include "dprintf.h"
4
5 #include <chrono>
6 #include <inttypes.h>
7 #include <memory>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <utility>
11
12 using namespace std;
13 using namespace std::chrono;
14
15 extern steady_clock::time_point start;
16
17 void apply_limit()
18 {
19         if (--limit_left > 0) {
20                 return;
21         }
22         dprintf("Done in %.1f ms, found %" PRId64 " matches.\n",
23                 1e3 * duration<float>(steady_clock::now() - start).count(), limit_matches);
24         if (only_count) {
25                 printf("%" PRId64 "\n", limit_matches);
26         }
27         exit(0);
28 }
29
30 void print_possibly_escaped(const string &str)
31 {
32         if (print_nul) {
33                 printf("%s%c", str.c_str(), 0);
34                 return;
35         } else if (literal_printing || !stdout_is_tty) {
36                 printf("%s\n", str.c_str());
37                 return;
38         }
39
40         // stdout is a terminal, so we should protect the user against
41         // escapes, stray newlines and the likes. First of all, check if
42         // all the characters are safe; we consider everything safe that
43         // isn't a control character, ', " or \. People could make
44         // filenames like "$(rm -rf)", but that's out-of-scope.
45         const char *ptr = str.data();
46         size_t len = str.size();
47
48         mbtowc(nullptr, 0, 0);
49         wchar_t pwc;
50         bool all_safe = true;
51         do {
52                 int ret = mbtowc(&pwc, ptr, len);
53                 if (ret == -1) {
54                         all_safe = false;  // Malformed data.
55                 } else if (ret == 0) {
56                         break;  // EOF.
57                 } else if (pwc < 32 || pwc == '\'' || pwc == '"' || pwc == '\\') {
58                         all_safe = false;
59                 } else if (pwc == '`') {
60                         // A rather odd case; ls quotes this but does not escape it.
61                         all_safe = false;
62                 } else {
63                         ptr += ret;
64                         len -= ret;
65                 }
66         } while (all_safe && *ptr != '\0');
67
68         if (all_safe) {
69                 printf("%s\n", str.c_str());
70                 return;
71         }
72
73         // Print escaped, but in such a way that the user can easily take the
74         // escaped output and paste into the shell. We print much like GNU ls does,
75         // ie., using the shell $'foo' construct whenever we need to print something
76         // escaped.
77         bool in_escaped_mode = false;
78         printf("'");
79
80         mbtowc(nullptr, 0, 0);
81         ptr = str.data();
82         len = str.size();
83         while (*ptr != '\0') {
84                 int ret = mbtowc(nullptr, ptr, len);
85                 if (ret == -1) {
86                         // Malformed data.
87                         printf("?");
88                         ++ptr;
89                         --len;
90                         continue;
91                 } else if (ret == 0) {
92                         break;  // EOF.
93                 }
94                 if ((unsigned char)*ptr < 32 || *ptr == '\'' || *ptr == '"' || *ptr == '\\') {
95                         if (!in_escaped_mode) {
96                                 printf("'$'");
97                                 in_escaped_mode = true;
98                         }
99
100                         // The list of allowed escapes is from bash(1).
101                         switch (*ptr) {
102                         case '\a':
103                                 printf("\\a");
104                                 break;
105                         case '\b':
106                                 printf("\\b");
107                                 break;
108                         case '\f':
109                                 printf("\\f");
110                                 break;
111                         case '\n':
112                                 printf("\\n");
113                                 break;
114                         case '\r':
115                                 printf("\\r");
116                                 break;
117                         case '\t':
118                                 printf("\\t");
119                                 break;
120                         case '\v':
121                                 printf("\\v");
122                                 break;
123                         case '\\':
124                                 printf("\\\\");
125                                 break;
126                         case '\'':
127                                 printf("\\'");
128                                 break;
129                         case '"':
130                                 printf("\\\"");
131                                 break;
132                         default:
133                                 printf("\\%03o", *ptr);
134                                 break;
135                         }
136                 } else {
137                         if (in_escaped_mode) {
138                                 printf("''");
139                                 in_escaped_mode = false;
140                         }
141                         fwrite(ptr, ret, 1, stdout);
142                 }
143                 ptr += ret;
144                 len -= ret;
145         }
146         printf("'\n");
147 }
148
149 void Serializer::print(uint64_t seq, uint64_t skip, const string msg)
150 {
151         if (only_count) {
152                 if (!msg.empty()) {
153                         apply_limit();
154                 }
155                 return;
156         }
157
158         if (next_seq != seq) {
159                 pending.push(Element{ seq, skip, move(msg) });
160                 return;
161         }
162
163         if (!msg.empty()) {
164                 print_possibly_escaped(msg);
165                 apply_limit();
166         }
167         next_seq += skip;
168
169         // See if any delayed prints can now be dealt with.
170         while (!pending.empty() && pending.top().seq == next_seq) {
171                 if (!pending.top().msg.empty()) {
172                         print_possibly_escaped(pending.top().msg);
173                         apply_limit();
174                 }
175                 next_seq += pending.top().skip;
176                 pending.pop();
177         }
178 }