File: serializer.cpp

package info (click to toggle)
plocate 1.1.8-2%2Bdeb11u1
  • links: PTS
  • area: main
  • in suites: bullseye
  • size: 468 kB
  • sloc: cpp: 5,341; sh: 106; makefile: 4
file content (178 lines) | stat: -rw-r--r-- 3,698 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include "serializer.h"

#include "dprintf.h"

#include <chrono>
#include <inttypes.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <utility>

using namespace std;
using namespace std::chrono;

extern steady_clock::time_point start;

void apply_limit()
{
	if (--limit_left > 0) {
		return;
	}
	dprintf("Done in %.1f ms, found %" PRId64 " matches.\n",
	        1e3 * duration<float>(steady_clock::now() - start).count(), limit_matches);
	if (only_count) {
		printf("%" PRId64 "\n", limit_matches);
	}
	exit(0);
}

void print_possibly_escaped(const string &str)
{
	if (print_nul) {
		printf("%s%c", str.c_str(), 0);
		return;
	} else if (!stdout_is_tty) {
		printf("%s\n", str.c_str());
		return;
	}

	// stdout is a terminal, so we should protect the user against
	// escapes, stray newlines and the likes. First of all, check if
	// all the characters are safe; we consider everything safe that
	// isn't a control character, ', " or \. People could make
	// filenames like "$(rm -rf)", but that's out-of-scope.
	const char *ptr = str.data();
	size_t len = str.size();

	mbtowc(nullptr, 0, 0);
	wchar_t pwc;
	bool all_safe = true;
	do {
		int ret = mbtowc(&pwc, ptr, len);
		if (ret == -1) {
			all_safe = false;  // Malformed data.
		} else if (ret == 0) {
			break;  // EOF.
		} else if (pwc < 32 || pwc == '\'' || pwc == '"' || pwc == '\\') {
			all_safe = false;
		} else if (pwc == '`') {
			// A rather odd case; ls quotes this but does not escape it.
			all_safe = false;
		} else {
			ptr += ret;
			len -= ret;
		}
	} while (all_safe && *ptr != '\0');

	if (all_safe) {
		printf("%s\n", str.c_str());
		return;
	}

	// Print escaped, but in such a way that the user can easily take the
	// escaped output and paste into the shell. We print much like GNU ls does,
	// ie., using the shell $'foo' construct whenever we need to print something
	// escaped.
	bool in_escaped_mode = false;
	printf("'");

	mbtowc(nullptr, 0, 0);
	ptr = str.data();
	len = str.size();
	while (*ptr != '\0') {
		int ret = mbtowc(nullptr, ptr, len);
		if (ret == -1) {
			// Malformed data.
			printf("?");
			++ptr;
			--len;
			continue;
		} else if (ret == 0) {
			break;  // EOF.
		}
		if ((unsigned char)*ptr < 32 || *ptr == '\'' || *ptr == '"' || *ptr == '\\') {
			if (!in_escaped_mode) {
				printf("'$'");
				in_escaped_mode = true;
			}

			// The list of allowed escapes is from bash(1).
			switch (*ptr) {
			case '\a':
				printf("\\a");
				break;
			case '\b':
				printf("\\b");
				break;
			case '\f':
				printf("\\f");
				break;
			case '\n':
				printf("\\n");
				break;
			case '\r':
				printf("\\r");
				break;
			case '\t':
				printf("\\t");
				break;
			case '\v':
				printf("\\v");
				break;
			case '\\':
				printf("\\\\");
				break;
			case '\'':
				printf("\\'");
				break;
			case '"':
				printf("\\\"");
				break;
			default:
				printf("\\%03o", *ptr);
				break;
			}
		} else {
			if (in_escaped_mode) {
				printf("''");
				in_escaped_mode = false;
			}
			fwrite(ptr, ret, 1, stdout);
		}
		ptr += ret;
		len -= ret;
	}
	printf("'\n");
}

void Serializer::print(uint64_t seq, uint64_t skip, const string msg)
{
	if (only_count) {
		if (!msg.empty()) {
			apply_limit();
		}
		return;
	}

	if (next_seq != seq) {
		pending.push(Element{ seq, skip, move(msg) });
		return;
	}

	if (!msg.empty()) {
		print_possibly_escaped(msg);
		apply_limit();
	}
	next_seq += skip;

	// See if any delayed prints can now be dealt with.
	while (!pending.empty() && pending.top().seq == next_seq) {
		if (!pending.top().msg.empty()) {
			print_possibly_escaped(pending.top().msg);
			apply_limit();
		}
		next_seq += pending.top().skip;
		pending.pop();
	}
}