xref: /freebsd/contrib/libdiff/test/test016.left.txt (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
1*59c8e88eSDag-Erling Smørgrav/*
2*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
3*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
4*59c8e88eSDag-Erling Smørgrav * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org>
5*59c8e88eSDag-Erling Smørgrav *
6*59c8e88eSDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any
7*59c8e88eSDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above
8*59c8e88eSDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies.
9*59c8e88eSDag-Erling Smørgrav *
10*59c8e88eSDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*59c8e88eSDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*59c8e88eSDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*59c8e88eSDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*59c8e88eSDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*59c8e88eSDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*59c8e88eSDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*59c8e88eSDag-Erling Smørgrav */
18*59c8e88eSDag-Erling Smørgrav
19*59c8e88eSDag-Erling Smørgrav#include <sys/queue.h>
20*59c8e88eSDag-Erling Smørgrav#include <sys/types.h>
21*59c8e88eSDag-Erling Smørgrav#include <sys/stat.h>
22*59c8e88eSDag-Erling Smørgrav#include <sys/param.h>
23*59c8e88eSDag-Erling Smørgrav#include <sys/wait.h>
24*59c8e88eSDag-Erling Smørgrav
25*59c8e88eSDag-Erling Smørgravstatic const struct got_error *
26*59c8e88eSDag-Erling Smørgravcmd_import(int argc, char *argv[])
27*59c8e88eSDag-Erling Smørgrav{
28*59c8e88eSDag-Erling Smørgrav	const struct got_error *error = NULL;
29*59c8e88eSDag-Erling Smørgrav	char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
30*59c8e88eSDag-Erling Smørgrav	char *gitconfig_path = NULL, *editor = NULL, *author = NULL;
31*59c8e88eSDag-Erling Smørgrav	const char *branch_name = "main";
32*59c8e88eSDag-Erling Smørgrav	char *refname = NULL, *id_str = NULL, *logmsg_path = NULL;
33*59c8e88eSDag-Erling Smørgrav	struct got_repository *repo = NULL;
34*59c8e88eSDag-Erling Smørgrav	struct got_reference *branch_ref = NULL, *head_ref = NULL;
35*59c8e88eSDag-Erling Smørgrav	struct got_object_id *new_commit_id = NULL;
36*59c8e88eSDag-Erling Smørgrav	int ch;
37*59c8e88eSDag-Erling Smørgrav	struct got_pathlist_head ignores;
38*59c8e88eSDag-Erling Smørgrav	struct got_pathlist_entry *pe;
39*59c8e88eSDag-Erling Smørgrav	int preserve_logmsg = 0;
40*59c8e88eSDag-Erling Smørgrav
41*59c8e88eSDag-Erling Smørgrav	TAILQ_INIT(&ignores);
42*59c8e88eSDag-Erling Smørgrav
43*59c8e88eSDag-Erling Smørgrav	while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
44*59c8e88eSDag-Erling Smørgrav		switch (ch) {
45*59c8e88eSDag-Erling Smørgrav		case 'b':
46*59c8e88eSDag-Erling Smørgrav			branch_name = optarg;
47*59c8e88eSDag-Erling Smørgrav			break;
48*59c8e88eSDag-Erling Smørgrav		case 'm':
49*59c8e88eSDag-Erling Smørgrav			logmsg = strdup(optarg);
50*59c8e88eSDag-Erling Smørgrav			if (logmsg == NULL) {
51*59c8e88eSDag-Erling Smørgrav				error = got_error_from_errno("strdup");
52*59c8e88eSDag-Erling Smørgrav				goto done;
53*59c8e88eSDag-Erling Smørgrav			}
54*59c8e88eSDag-Erling Smørgrav			break;
55*59c8e88eSDag-Erling Smørgrav		case 'r':
56*59c8e88eSDag-Erling Smørgrav			repo_path = realpath(optarg, NULL);
57*59c8e88eSDag-Erling Smørgrav			if (repo_path == NULL) {
58*59c8e88eSDag-Erling Smørgrav				error = got_error_from_errno2("realpath",
59*59c8e88eSDag-Erling Smørgrav				    optarg);
60*59c8e88eSDag-Erling Smørgrav				goto done;
61*59c8e88eSDag-Erling Smørgrav			}
62*59c8e88eSDag-Erling Smørgrav			break;
63*59c8e88eSDag-Erling Smørgrav		case 'I':
64*59c8e88eSDag-Erling Smørgrav			if (optarg[0] == '\0')
65*59c8e88eSDag-Erling Smørgrav				break;
66*59c8e88eSDag-Erling Smørgrav			error = got_pathlist_insert(&pe, &ignores, optarg,
67*59c8e88eSDag-Erling Smørgrav			    NULL);
68*59c8e88eSDag-Erling Smørgrav			if (error)
69*59c8e88eSDag-Erling Smørgrav				goto done;
70*59c8e88eSDag-Erling Smørgrav			break;
71*59c8e88eSDag-Erling Smørgrav		default:
72*59c8e88eSDag-Erling Smørgrav			usage_import();
73*59c8e88eSDag-Erling Smørgrav			/* NOTREACHED */
74*59c8e88eSDag-Erling Smørgrav		}
75*59c8e88eSDag-Erling Smørgrav	}
76*59c8e88eSDag-Erling Smørgrav
77*59c8e88eSDag-Erling Smørgrav#ifndef PROFILE
78*59c8e88eSDag-Erling Smørgrav	if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
79*59c8e88eSDag-Erling Smørgrav	    "unveil",
80*59c8e88eSDag-Erling Smørgrav	    NULL) == -1)
81*59c8e88eSDag-Erling Smørgrav		err(1, "pledge");
82*59c8e88eSDag-Erling Smørgrav#endif
83*59c8e88eSDag-Erling Smørgrav	if (argc != 1)
84*59c8e88eSDag-Erling Smørgrav		usage_import();
85*59c8e88eSDag-Erling Smørgrav
86*59c8e88eSDag-Erling Smørgrav	if (repo_path == NULL) {
87*59c8e88eSDag-Erling Smørgrav		repo_path = getcwd(NULL, 0);
88*59c8e88eSDag-Erling Smørgrav		if (repo_path == NULL)
89*59c8e88eSDag-Erling Smørgrav			return got_error_from_errno("getcwd");
90*59c8e88eSDag-Erling Smørgrav	}
91*59c8e88eSDag-Erling Smørgrav	error = get_gitconfig_path(&gitconfig_path);
92*59c8e88eSDag-Erling Smørgrav	if (error)
93*59c8e88eSDag-Erling Smørgrav		goto done;
94*59c8e88eSDag-Erling Smørgrav	error = got_repo_open(&repo, repo_path, gitconfig_path);
95*59c8e88eSDag-Erling Smørgrav	if (error)
96*59c8e88eSDag-Erling Smørgrav		goto done;
97*59c8e88eSDag-Erling Smørgrav
98*59c8e88eSDag-Erling Smørgrav	error = get_author(&author, repo, NULL);
99*59c8e88eSDag-Erling Smørgrav	if (error)
100*59c8e88eSDag-Erling Smørgrav		return error;
101*59c8e88eSDag-Erling Smørgrav
102*59c8e88eSDag-Erling Smørgrav	/*
103*59c8e88eSDag-Erling Smørgrav	 * Don't let the user create a branch name with a leading '-'.
104*59c8e88eSDag-Erling Smørgrav	 * While technically a valid reference name, this case is usually
105*59c8e88eSDag-Erling Smørgrav	 * an unintended typo.
106*59c8e88eSDag-Erling Smørgrav	 */
107*59c8e88eSDag-Erling Smørgrav	if (branch_name[0] == '-')
108*59c8e88eSDag-Erling Smørgrav		return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS);
109*59c8e88eSDag-Erling Smørgrav
110*59c8e88eSDag-Erling Smørgrav	if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
111*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno("asprintf");
112*59c8e88eSDag-Erling Smørgrav		goto done;
113*59c8e88eSDag-Erling Smørgrav	}
114*59c8e88eSDag-Erling Smørgrav
115*59c8e88eSDag-Erling Smørgrav	error = got_ref_open(&branch_ref, repo, refname, 0);
116*59c8e88eSDag-Erling Smørgrav	if (error) {
117*59c8e88eSDag-Erling Smørgrav		if (error->code != GOT_ERR_NOT_REF)
118*59c8e88eSDag-Erling Smørgrav			goto done;
119*59c8e88eSDag-Erling Smørgrav	} else {
120*59c8e88eSDag-Erling Smørgrav		error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
121*59c8e88eSDag-Erling Smørgrav		    "import target branch already exists");
122*59c8e88eSDag-Erling Smørgrav		goto done;
123*59c8e88eSDag-Erling Smørgrav	}
124*59c8e88eSDag-Erling Smørgrav
125*59c8e88eSDag-Erling Smørgrav	path_dir = realpath(argv[0], NULL);
126*59c8e88eSDag-Erling Smørgrav	if (path_dir == NULL) {
127*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno2("realpath", argv[0]);
128*59c8e88eSDag-Erling Smørgrav		goto done;
129*59c8e88eSDag-Erling Smørgrav	}
130*59c8e88eSDag-Erling Smørgrav	got_path_strip_trailing_slashes(path_dir);
131*59c8e88eSDag-Erling Smørgrav
132*59c8e88eSDag-Erling Smørgrav	/*
133*59c8e88eSDag-Erling Smørgrav	 * unveil(2) traverses exec(2); if an editor is used we have
134*59c8e88eSDag-Erling Smørgrav	 * to apply unveil after the log message has been written.
135*59c8e88eSDag-Erling Smørgrav	 */
136*59c8e88eSDag-Erling Smørgrav	if (logmsg == NULL || strlen(logmsg) == 0) {
137*59c8e88eSDag-Erling Smørgrav		error = get_editor(&editor);
138*59c8e88eSDag-Erling Smørgrav		if (error)
139*59c8e88eSDag-Erling Smørgrav			goto done;
140*59c8e88eSDag-Erling Smørgrav		free(logmsg);
141*59c8e88eSDag-Erling Smørgrav		error = collect_import_msg(&logmsg, &logmsg_path, editor,
142*59c8e88eSDag-Erling Smørgrav		    path_dir, refname);
143*59c8e88eSDag-Erling Smørgrav		if (error) {
144*59c8e88eSDag-Erling Smørgrav			if (error->code != GOT_ERR_COMMIT_MSG_EMPTY &&
145*59c8e88eSDag-Erling Smørgrav			    logmsg_path != NULL)
146*59c8e88eSDag-Erling Smørgrav				preserve_logmsg = 1;
147*59c8e88eSDag-Erling Smørgrav			goto done;
148*59c8e88eSDag-Erling Smørgrav		}
149*59c8e88eSDag-Erling Smørgrav	}
150*59c8e88eSDag-Erling Smørgrav
151*59c8e88eSDag-Erling Smørgrav	if (unveil(path_dir, "r") != 0) {
152*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno2("unveil", path_dir);
153*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
154*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
155*59c8e88eSDag-Erling Smørgrav		goto done;
156*59c8e88eSDag-Erling Smørgrav	}
157*59c8e88eSDag-Erling Smørgrav
158*59c8e88eSDag-Erling Smørgrav	error = apply_unveil(got_repo_get_path(repo), 0, NULL);
159*59c8e88eSDag-Erling Smørgrav	if (error) {
160*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
161*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
162*59c8e88eSDag-Erling Smørgrav		goto done;
163*59c8e88eSDag-Erling Smørgrav	}
164*59c8e88eSDag-Erling Smørgrav
165*59c8e88eSDag-Erling Smørgrav	error = got_repo_import(&new_commit_id, path_dir, logmsg,
166*59c8e88eSDag-Erling Smørgrav	    author, &ignores, repo, import_progress, NULL);
167*59c8e88eSDag-Erling Smørgrav	if (error) {
168*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
169*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
170*59c8e88eSDag-Erling Smørgrav		goto done;
171*59c8e88eSDag-Erling Smørgrav	}
172*59c8e88eSDag-Erling Smørgrav
173*59c8e88eSDag-Erling Smørgrav	error = got_ref_alloc(&branch_ref, refname, new_commit_id);
174*59c8e88eSDag-Erling Smørgrav	if (error) {
175*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
176*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
177*59c8e88eSDag-Erling Smørgrav		goto done;
178*59c8e88eSDag-Erling Smørgrav	}
179*59c8e88eSDag-Erling Smørgrav
180*59c8e88eSDag-Erling Smørgrav	error = got_ref_write(branch_ref, repo);
181*59c8e88eSDag-Erling Smørgrav	if (error) {
182*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
183*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
184*59c8e88eSDag-Erling Smørgrav		goto done;
185*59c8e88eSDag-Erling Smørgrav	}
186*59c8e88eSDag-Erling Smørgrav
187*59c8e88eSDag-Erling Smørgrav	error = got_object_id_str(&id_str, new_commit_id);
188*59c8e88eSDag-Erling Smørgrav	if (error) {
189*59c8e88eSDag-Erling Smørgrav		if (logmsg_path)
190*59c8e88eSDag-Erling Smørgrav			preserve_logmsg = 1;
191*59c8e88eSDag-Erling Smørgrav		goto done;
192*59c8e88eSDag-Erling Smørgrav	}
193*59c8e88eSDag-Erling Smørgrav
194*59c8e88eSDag-Erling Smørgrav	error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
195*59c8e88eSDag-Erling Smørgrav	if (error) {
196*59c8e88eSDag-Erling Smørgrav		if (error->code != GOT_ERR_NOT_REF) {
197*59c8e88eSDag-Erling Smørgrav			if (logmsg_path)
198*59c8e88eSDag-Erling Smørgrav				preserve_logmsg = 1;
199*59c8e88eSDag-Erling Smørgrav			goto done;
200*59c8e88eSDag-Erling Smørgrav		}
201*59c8e88eSDag-Erling Smørgrav
202*59c8e88eSDag-Erling Smørgrav		error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
203*59c8e88eSDag-Erling Smørgrav		    branch_ref);
204*59c8e88eSDag-Erling Smørgrav		if (error) {
205*59c8e88eSDag-Erling Smørgrav			if (logmsg_path)
206*59c8e88eSDag-Erling Smørgrav				preserve_logmsg = 1;
207*59c8e88eSDag-Erling Smørgrav			goto done;
208*59c8e88eSDag-Erling Smørgrav		}
209*59c8e88eSDag-Erling Smørgrav
210*59c8e88eSDag-Erling Smørgrav		error = got_ref_write(head_ref, repo);
211*59c8e88eSDag-Erling Smørgrav		if (error) {
212*59c8e88eSDag-Erling Smørgrav			if (logmsg_path)
213*59c8e88eSDag-Erling Smørgrav				preserve_logmsg = 1;
214*59c8e88eSDag-Erling Smørgrav			goto done;
215*59c8e88eSDag-Erling Smørgrav		}
216*59c8e88eSDag-Erling Smørgrav	}
217*59c8e88eSDag-Erling Smørgrav
218*59c8e88eSDag-Erling Smørgrav	printf("Created branch %s with commit %s\n",
219*59c8e88eSDag-Erling Smørgrav	    got_ref_get_name(branch_ref), id_str);
220*59c8e88eSDag-Erling Smørgravdone:
221*59c8e88eSDag-Erling Smørgrav	if (preserve_logmsg) {
222*59c8e88eSDag-Erling Smørgrav		fprintf(stderr, "%s: log message preserved in %s\n",
223*59c8e88eSDag-Erling Smørgrav		    getprogname(), logmsg_path);
224*59c8e88eSDag-Erling Smørgrav	} else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL)
225*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno2("unlink", logmsg_path);
226*59c8e88eSDag-Erling Smørgrav	free(logmsg);
227*59c8e88eSDag-Erling Smørgrav	free(logmsg_path);
228*59c8e88eSDag-Erling Smørgrav	free(repo_path);
229*59c8e88eSDag-Erling Smørgrav	free(editor);
230*59c8e88eSDag-Erling Smørgrav	free(refname);
231*59c8e88eSDag-Erling Smørgrav	free(new_commit_id);
232*59c8e88eSDag-Erling Smørgrav	free(id_str);
233*59c8e88eSDag-Erling Smørgrav	free(author);
234*59c8e88eSDag-Erling Smørgrav	free(gitconfig_path);
235*59c8e88eSDag-Erling Smørgrav	if (branch_ref)
236*59c8e88eSDag-Erling Smørgrav		got_ref_close(branch_ref);
237*59c8e88eSDag-Erling Smørgrav	if (head_ref)
238*59c8e88eSDag-Erling Smørgrav		got_ref_close(head_ref);
239*59c8e88eSDag-Erling Smørgrav	return error;
240*59c8e88eSDag-Erling Smørgrav}
241*59c8e88eSDag-Erling Smørgrav
242*59c8e88eSDag-Erling Smørgrav__dead static void
243*59c8e88eSDag-Erling Smørgravusage_clone(void)
244*59c8e88eSDag-Erling Smørgrav{
245*59c8e88eSDag-Erling Smørgrav	fprintf(stderr, "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] "
246*59c8e88eSDag-Erling Smørgrav	    "[-R reference] repository-url [directory]\n", getprogname());
247*59c8e88eSDag-Erling Smørgrav	exit(1);
248*59c8e88eSDag-Erling Smørgrav}
249*59c8e88eSDag-Erling Smørgrav
250*59c8e88eSDag-Erling Smørgravstatic const struct got_error *
251*59c8e88eSDag-Erling Smørgravcmd_clone(int argc, char *argv[])
252*59c8e88eSDag-Erling Smørgrav{
253*59c8e88eSDag-Erling Smørgrav	const struct got_error *error = NULL;
254*59c8e88eSDag-Erling Smørgrav	const char *uri, *dirname;
255*59c8e88eSDag-Erling Smørgrav	char *proto, *host, *port, *repo_name, *server_path;
256*59c8e88eSDag-Erling Smørgrav	char *default_destdir = NULL, *id_str = NULL;
257*59c8e88eSDag-Erling Smørgrav	const char *repo_path;
258*59c8e88eSDag-Erling Smørgrav	struct got_repository *repo = NULL;
259*59c8e88eSDag-Erling Smørgrav	struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
260*59c8e88eSDag-Erling Smørgrav	struct got_pathlist_entry *pe;
261*59c8e88eSDag-Erling Smørgrav	struct got_object_id *pack_hash = NULL;
262*59c8e88eSDag-Erling Smørgrav	int ch, fetchfd = -1, fetchstatus;
263*59c8e88eSDag-Erling Smørgrav	pid_t fetchpid = -1;
264*59c8e88eSDag-Erling Smørgrav	x
265*59c8e88eSDag-Erling Smørgrav
266*59c8e88eSDag-Erling Smørgrav	/* Create got.conf(5). */
267*59c8e88eSDag-Erling Smørgrav	gotconfig_path = got_repo_get_path_gotconfig(repo);
268*59c8e88eSDag-Erling Smørgrav	if (gotconfig_path == NULL) {
269*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno("got_repo_get_path_gotconfig");
270*59c8e88eSDag-Erling Smørgrav		goto done;
271*59c8e88eSDag-Erling Smørgrav	}
272*59c8e88eSDag-Erling Smørgrav	gotconfig_file = fopen(gotconfig_path, "a");
273*59c8e88eSDag-Erling Smørgrav	if (gotconfig_file == NULL) {
274*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno2("fopen", gotconfig_path);
275*59c8e88eSDag-Erling Smørgrav		goto done;
276*59c8e88eSDag-Erling Smørgrav	}
277*59c8e88eSDag-Erling Smørgrav	got_path_strip_trailing_slashes(server_path);
278*59c8e88eSDag-Erling Smørgrav	if (asprintf(&gotconfig,
279*59c8e88eSDag-Erling Smørgrav	    "remote \"%s\" {\n"
280*59c8e88eSDag-Erling Smørgrav	    "\tserver %s\n"
281*59c8e88eSDag-Erling Smørgrav	    "\tprotocol %s\n"
282*59c8e88eSDag-Erling Smørgrav	    "%s%s%s"
283*59c8e88eSDag-Erling Smørgrav	    "\trepository \"%s\"\n"
284*59c8e88eSDag-Erling Smørgrav	    "%s"
285*59c8e88eSDag-Erling Smørgrav	    "}\n",
286*59c8e88eSDag-Erling Smørgrav	    GOT_FETCH_DEFAULT_REMOTE_NAME, host, proto,
287*59c8e88eSDag-Erling Smørgrav	    port ? "\tport " : "", port ? port : "", port ? "\n" : "",
288*59c8e88eSDag-Erling Smørgrav	    server_path,
289*59c8e88eSDag-Erling Smørgrav	    mirror_references ? "\tmirror-references yes\n" : "") == -1) {
290*59c8e88eSDag-Erling Smørgrav		error = got_error_from_errno("asprintf");
291*59c8e88eSDag-Erling Smørgrav		goto done;
292*59c8e88eSDag-Erling Smørgrav	}
293*59c8e88eSDag-Erling Smørgrav	n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
294*59c8e88eSDag-Erling Smørgrav	if (n != strlen(gotconfig)) {
295*59c8e88eSDag-Erling Smørgrav		error = got_ferror(gotconfig_file, GOT_ERR_IO);
296*59c8e88eSDag-Erling Smørgrav		goto done;
297*59c8e88eSDag-Erling Smørgrav	}
298*59c8e88eSDag-Erling Smørgrav}
299