Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_fixcpp/sys/src/cmd/auth/factotum/fgui.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "dat.h"
2
#include <draw.h>
3
#include <mouse.h>
4
#include <keyboard.h>
5
#include <control.h>
6
 
7
int ctldeletequits = 1;
8
 
9
typedef struct RequestType RequestType;
10
typedef struct Request Request;
11
typedef struct Memory Memory;
12
 
13
struct RequestType
14
{
15
	char		*file;			/* file to read requests from */
16
	void		(*f)(Request*);		/* request handler */
17
	void		(*r)(Controlset*);	/* resize handler */
18
	int		fd;			/* fd = open(file, ORDWR) */
19
	Channel		*rc;			/* channel requests are multiplexed to */
20
	Controlset	*cs;
21
};
22
 
23
struct Request
24
{
25
	RequestType	*rt;
26
	Attr		*a;
27
	Attr		*tag;
28
};
29
 
30
struct Memory
31
{
32
	Memory	*next;
33
	Attr	*a;
34
	Attr	*val;
35
};
36
Memory *mem;
37
 
38
static void	readreq(void*);
39
static void	hide(void);
40
static void	unhide(void);
41
static void	openkmr(void);
42
static void	closekmr(void);
43
static Memory*	searchmem(Attr*);
44
static void	addmem(Attr*, Attr*);
45
 
46
static void	confirm(Request*);
47
static void	resizeconfirm(Controlset*);
48
static void	needkey(Request*);
49
static void	resizeneedkey(Controlset*);
50
 
51
Control *b_remember;
52
Control *b_accept;
53
Control *b_refuse;
54
 
55
RequestType rt[] = 
56
{
57
	{ "/mnt/factotum/confirm",	confirm,	resizeconfirm, },
58
	{ "/mnt/factotum/needkey",	needkey,	resizeneedkey, },
59
	{ 0 },
60
};
61
 
62
enum
63
{
64
	ButtonDim=	15,
65
};
66
 
67
void
68
threadmain(int argc, char *argv[])
69
{
70
	Request r;
71
	Channel *rc;
72
	RequestType *p;
73
	Font *invis;
74
 
75
	ARGBEGIN{
76
	}ARGEND;
77
 
78
	if(newwindow("-hide") < 0)
79
		sysfatal("newwindow: %r");
80
 
81
	fmtinstall('A', _attrfmt);
82
 
83
	/* create the proc's that read */
84
	rc = chancreate(sizeof(Request), 0);
85
	for(p = rt;  p->file != 0; p++){
86
		p->fd = -1;
87
		p->rc = rc;
88
		proccreate(readreq, p, 16*1024);
89
	}
90
 
91
	/* gui initialization */
92
	if(initdraw(0, 0, "auth/fgui") < 0)
93
		sysfatal("initdraw failed: %r");
94
	initcontrols();
95
	hide();
96
 
97
	/* get an invisible font for passwords */
98
	invis = openfont(display, "/lib/font/bit/lucm/passwd.9.font");
99
	if (invis == nil)
100
		sysfatal("fgui: %s: %r", "/lib/font/bit/lucm/passwd.9.font");
101
	namectlfont(invis, "invisible");
102
 
103
	/* serialize all requests */
104
	for(;;){
105
		if(recv(rc, &r) < 0)
106
			break;
107
		(*r.rt->f)(&r);
108
		_freeattr(r.a);
109
		_freeattr(r.tag);
110
	}
111
 
112
	threadexitsall(nil);
113
}
114
 
115
/*
116
 *  read requests and pass them to the main loop
117
 */
118
enum
119
{
120
	Requestlen=4096,
121
};
122
static void
123
readreq(void *a)
124
{
125
	RequestType *rt = a;
126
	char *buf, *p;
127
	int n;
128
	Request r;
129
	Attr **l;
130
 
131
	rt->fd = open(rt->file, ORDWR);
132
	if(rt->fd < 0)
133
		sysfatal("opening %s: %r", rt->file);
134
	rt->cs = nil;
135
 
136
	buf = malloc(Requestlen);
137
	if(buf == nil)
138
		sysfatal("allocating read buffer: %r");
139
	r.rt = rt;
140
 
141
	for(;;){
142
		n = read(rt->fd, buf, Requestlen-1);
143
		if(n < 0)
144
			break;
145
		buf[n] = 0;
146
 
147
		/* skip verb, parse attributes, and remove tag */
148
		p = strchr(buf, ' ');
149
		if(p != nil)
150
			p++;
151
		else
152
			p = buf;
153
		r.a = _parseattr(p);
154
 
155
		/* separate out the tag */
156
		r.tag = nil;
157
		for(l = &r.a; *l != nil; l = &(*l)->next)
158
			if(strcmp((*l)->name, "tag") == 0){
159
				r.tag = *l;
160
				*l = r.tag->next;
161
				r.tag->next = nil;
162
				break;
163
			}
164
 
165
		/* if no tag, forget it */
166
		if(r.tag == nil){
167
			_freeattr(r.a);
168
			continue;
169
		}
170
 
171
		send(rt->rc, &r);
172
	}
173
}
174
#ifdef asdf
175
static void
176
readreq(void *a)
177
{
178
	RequestType *rt = a;
179
	char *buf, *p;
180
	int n;
181
	Request r;
182
 
183
	rt->fd = -1;
184
	rt->cs = nil;
185
 
186
	buf = malloc(Requestlen);
187
	if(buf == nil)
188
		sysfatal("allocating read buffer: %r");
189
	r.rt = rt;
190
 
191
	for(;;){
192
		strcpy(buf, "adfasdf=afdasdf asdfasdf=asdfasdf");
193
		r.a = _parseattr(buf);
194
		send(rt->rc, &r);
195
		sleep(5000);
196
	}
197
}
198
#endif asdf
199
 
200
/*
201
 *  open/close the keyboard, mouse and resize channels
202
 */
203
static Channel *kbdc;
204
static Channel *mousec;
205
static Channel *resizec;
206
static Keyboardctl *kctl;
207
static Mousectl *mctl;
208
 
209
static void
210
openkmr(void)
211
{
212
	/* get channels for subsequent newcontrolset calls  */
213
	kctl = initkeyboard(nil);
214
	if(kctl == nil)
215
		sysfatal("can't initialize keyboard: %r");
216
	kbdc = kctl->c;
217
	mctl = initmouse(nil, screen);
218
	if(mctl == nil)
219
		sysfatal("can't initialize mouse: %r");
220
	mousec = mctl->c;
221
	resizec = mctl->resizec;
222
}
223
static void
224
closekmr(void)
225
{
226
	Mouse m;
227
 
228
	while(nbrecv(kbdc, &m) > 0)
229
		;
230
	closekeyboard(kctl);
231
	while(nbrecv(mousec, &m) > 0)
232
		;
233
	closemouse(mctl);
234
}
235
 
236
 
237
/*
238
 *  called when the window is resized
239
 */
240
void
241
resizecontrolset(Controlset *cs)
242
{
243
	RequestType *p;
244
 
245
	for(p = rt;  p->file != 0; p++){
246
		if(p->cs == cs){
247
			(*p->r)(cs);
248
			break;
249
		}
250
	}
251
}
252
 
253
/*
254
 *  hide window when not in use
255
 */
256
static void
257
unhide(void)
258
{
259
	int wctl;
260
 
261
	wctl = open("/dev/wctl", OWRITE);
262
	if(wctl < 0)
263
		return;
264
	fprint(wctl, "unhide");
265
	close(wctl);
266
}
267
static void
268
hide(void)
269
{
270
	int wctl;
271
	int tries;
272
 
273
	wctl = open("/dev/wctl", OWRITE);
274
	if(wctl < 0)
275
		return;
276
	for(tries = 0; tries < 10; tries++){
277
		if(fprint(wctl, "hide") >= 0)
278
			break;
279
		sleep(100);
280
	}
281
	close(wctl);
282
}
283
 
284
/*
285
 *  set up the controls for the confirmation window
286
 */
287
static Channel*
288
setupconfirm(Request *r)
289
{
290
	Controlset *cs;
291
	Channel *c;
292
	Attr *a;
293
 
294
	/* create a new control set for the confirmation */
295
	openkmr();
296
	cs = newcontrolset(screen, kbdc, mousec, resizec);
297
 
298
	createtext(cs, "msg");
299
	chanprint(cs->ctl, "msg image paleyellow");
300
	chanprint(cs->ctl, "msg border 1");
301
	chanprint(cs->ctl, "msg add 'The following key is being used:'");
302
	for(a = r->a; a != nil; a = a->next)
303
		chanprint(cs->ctl, "msg add '    %s = %s'", a->name,
304
				a->val);
305
 
306
	namectlimage(display->white, "i_white");
307
	namectlimage(display->black, "i_black");
308
 
309
	b_remember = createbutton(cs, "b_remember");
310
	chanprint(cs->ctl, "b_remember border 1");
311
	chanprint(cs->ctl, "b_remember mask i_white");
312
	chanprint(cs->ctl, "b_remember image i_white");
313
	chanprint(cs->ctl, "b_remember light i_black");
314
 
315
	createtext(cs, "t_remember");
316
	chanprint(cs->ctl, "t_remember image white");
317
	chanprint(cs->ctl, "t_remember bordercolor white");
318
	chanprint(cs->ctl, "t_remember add 'Remember this answer for future confirmations'");
319
 
320
	b_accept = createtextbutton(cs, "b_accept");
321
	chanprint(cs->ctl, "b_accept border 1");
322
	chanprint(cs->ctl, "b_accept align center");
323
	chanprint(cs->ctl, "b_accept text Accept");
324
	chanprint(cs->ctl, "b_accept image i_white");
325
	chanprint(cs->ctl, "b_accept light i_black");
326
 
327
	b_refuse = createtextbutton(cs, "b_refuse");
328
	chanprint(cs->ctl, "b_refuse border 1");
329
	chanprint(cs->ctl, "b_refuse align center");
330
	chanprint(cs->ctl, "b_refuse text Refuse");
331
	chanprint(cs->ctl, "b_refuse image i_white");
332
	chanprint(cs->ctl, "b_refuse light i_black");
333
 
334
	c = chancreate(sizeof(char*), 0);
335
	controlwire(b_remember, "event", c);
336
	controlwire(b_accept, "event", c);
337
	controlwire(b_refuse, "event", c);
338
 
339
	/* make the controls interactive */
340
	activate(b_remember);
341
	activate(b_accept);
342
	activate(b_refuse);
343
	r->rt->cs = cs;
344
	unhide();
345
	resizecontrolset(cs);
346
 
347
	return c;
348
}
349
 
350
/*
351
 *  resize the controls for the confirmation window
352
 */
353
static void
354
resizeconfirm(Controlset *cs)
355
{
356
	Rectangle r, mr, nr, ntr, ar, rr;
357
	int fontwidth;
358
 
359
	fontwidth = font->height;
360
 
361
	/* get usable window rectangle */
362
	if(getwindow(display, Refnone) < 0)
363
		ctlerror("resize failed: %r");
364
	r = insetrect(screen->r, 10);
365
 
366
	/* message box fills everything not needed for buttons */
367
	mr = r;
368
	mr.max.y = mr.min.y + font->height*((Dy(mr)-3*ButtonDim-font->height-4)/font->height);
369
 
370
	/* remember button */
371
	nr.min = Pt(mr.min.x, mr.max.y+ButtonDim);
372
	nr.max = Pt(mr.max.x, r.max.y);
373
	if(Dx(nr) > ButtonDim)
374
		nr.max.x = nr.min.x+ButtonDim;
375
	if(Dy(nr) > ButtonDim)
376
		nr.max.y = nr.min.y+ButtonDim;
377
	ntr.min = Pt(nr.max.x+ButtonDim, nr.min.y);
378
	ntr.max = Pt(r.max.x, nr.min.y+font->height);
379
 
380
	/* accept/refuse buttons */
381
	ar.min = Pt(r.min.x+Dx(r)/2-ButtonDim-6*fontwidth, nr.max.y+ButtonDim);
382
	ar.max = Pt(ar.min.x+6*fontwidth, ar.min.y+font->height+4);
383
	rr.min = Pt(r.min.x+Dx(r)/2+ButtonDim, nr.max.y+ButtonDim);
384
	rr.max = Pt(rr.min.x+6*fontwidth, rr.min.y+font->height+4);
385
 
386
	/* make the controls visible */
387
	chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
388
	chanprint(cs->ctl, "b_remember rect %R\nb_remember show", nr);
389
	chanprint(cs->ctl, "t_remember rect %R\nt_remember show", ntr);
390
	chanprint(cs->ctl, "b_accept rect %R\nb_accept show", ar);
391
	chanprint(cs->ctl, "b_refuse rect %R\nb_refuse show", rr);
392
}
393
 
394
/*
395
 *  free the controls for the confirmation window
396
 */
397
static void
398
teardownconfirm(Request *r)
399
{
400
	Controlset *cs;
401
 
402
	cs = r->rt->cs;
403
	r->rt->cs = nil;
404
	hide();
405
	closecontrolset(cs);
406
	closekmr();
407
}
408
 
409
/*
410
 *  get user confirmation of a key
411
 */
412
static void
413
confirm(Request *r)
414
{
415
	Channel *c;
416
	char *s;
417
	int n;
418
	char *args[3];
419
	int remember;
420
	Attr *val;
421
	Memory *m;
422
 
423
	/* if it's something that the user wanted us not to ask again about */
424
	m = searchmem(r->a);
425
	if(m != nil){
426
		fprint(r->rt->fd, "%A %A", r->tag, m->val);
427
		return;
428
	}
429
 
430
	/* set up the controls */
431
	c = setupconfirm(r);
432
 
433
	/* wait for user to reply */
434
	remember = 0;
435
	for(;;){
436
		s = recvp(c);
437
		n = tokenize(s, args, nelem(args));
438
		if(n==3 && strcmp(args[1], "value")==0){
439
			if(strcmp(args[0], "b_remember:") == 0){
440
				remember = atoi(args[2]);
441
			}
442
			if(strcmp(args[0], "b_accept:") == 0){
443
				val = _mkattr(AttrNameval, "answer", "yes", nil);
444
				free(s);
445
				break;
446
			}
447
			if(strcmp(args[0], "b_refuse:") == 0){
448
				val = _mkattr(AttrNameval, "answer", "no", nil);
449
				free(s);
450
				break;
451
			}
452
		}
453
		free(s);
454
	}
455
	teardownconfirm(r);
456
	fprint(r->rt->fd, "%A %A", r->tag, val);
457
	if(remember)
458
		addmem(_copyattr(r->a), val);
459
	else
460
		_freeattr(val);
461
}
462
 
463
/*
464
 *  confirmations that are remembered
465
 */
466
static int
467
match(Attr *a, Attr *b)
468
{
469
	Attr *x;
470
 
471
	for(; a != nil; a = a->next){
472
		x = _findattr(b, a->name);
473
		if(x == nil || strcmp(a->val, x->val) != 0)
474
			return 0;
475
	}
476
	return 1;
477
}
478
static Memory*
479
searchmem(Attr *a)
480
{
481
	Memory *m;
482
 
483
	for(m = mem; m != nil; m = m->next){
484
		if(match(a, m->a))
485
			break;
486
	}
487
	return m;
488
}
489
static void
490
addmem(Attr *a, Attr *val)
491
{
492
	Memory *m;
493
 
494
	m = malloc(sizeof *m);
495
	if(m == nil)
496
		return;
497
	m->a = a;
498
	m->val = val;
499
	m->next = mem;
500
	mem = m;
501
}
502
 
503
/* controls for needkey */
504
Control *msg;
505
Control *b_done;
506
enum {
507
	Pprivate=	1<<0,
508
	Pneed=		1<<1,
509
};
510
typedef struct Entry Entry;
511
struct Entry {
512
	Control *name;
513
	Control *val;
514
	Control *eq;
515
	Attr *a;
516
};
517
static Entry *entry;
518
static int entries;
519
 
520
/*
521
 *  set up the controls for the confirmation window
522
 */
523
static Channel*
524
setupneedkey(Request *r)
525
{
526
	Controlset *cs;
527
	Channel *c;
528
	Attr *a;
529
	Attr **l;
530
	char cn[10];
531
	int i;
532
 
533
	/* create a new control set for the confirmation */
534
	openkmr();
535
	cs = newcontrolset(screen, kbdc, mousec, resizec);
536
 
537
	/* count attributes and allocate entry controls */
538
	entries = 0;
539
	for(l = &r->a; *l; l = &(*l)->next)
540
		entries++;
541
	if(entries == 0){
542
		closecontrolset(cs);
543
		closekmr();
544
		return nil;
545
	}
546
	*l = a = mallocz(sizeof *a, 1);
547
	a->type = AttrQuery;
548
	entries++;
549
	l = &(*l)->next;
550
	*l = a = mallocz(sizeof *a, 1);
551
	a->type = AttrQuery;
552
	entries++;
553
	entry = malloc(entries*sizeof(Entry));
554
	if(entry == nil){
555
		closecontrolset(cs);
556
		closekmr();
557
		return nil;
558
	}
559
 
560
	namectlimage(display->white, "i_white");
561
	namectlimage(display->black, "i_black");
562
 
563
	/* create controls */
564
	msg = createtext(cs, "msg");
565
	chanprint(cs->ctl, "msg image white");
566
	chanprint(cs->ctl, "msg bordercolor white");
567
	chanprint(cs->ctl, "msg add 'You need the following key.  Fill in the blanks'");
568
	chanprint(cs->ctl, "msg add 'and click on the DONE button.'");
569
 
570
	for(i = 0, a = r->a; a != nil; i++, a = a->next){
571
		entry[i].a = a;
572
		snprint(cn, sizeof cn, "name_%d", i);
573
		if(entry[i].a->name == nil){
574
			entry[i].name = createentry(cs, cn);
575
			chanprint(cs->ctl, "%s image yellow", cn);
576
			chanprint(cs->ctl, "%s border 1", cn);
577
		} else {
578
			entry[i].name = createtext(cs, cn);
579
			chanprint(cs->ctl, "%s image white", cn);
580
			chanprint(cs->ctl, "%s bordercolor white", cn);
581
			chanprint(cs->ctl, "%s add '%s'", cn, a->name);
582
		}
583
 
584
		snprint(cn, sizeof cn, "val_%d", i);
585
		if(a->type == AttrQuery){
586
			entry[i].val = createentry(cs, cn);
587
			chanprint(cs->ctl, "%s image yellow", cn);
588
			chanprint(cs->ctl, "%s border 1", cn);
589
			if(a->name != nil){
590
				if(strcmp(a->name, "user") == 0)
591
					chanprint(cs->ctl, "%s value %q", cn, getuser());
592
				if(*a->name == '!')
593
					chanprint(cs->ctl, "%s font invisible", cn);
594
			}
595
		} else {
596
			entry[i].val = createtext(cs, cn);
597
			chanprint(cs->ctl, "%s image white", cn);
598
			chanprint(cs->ctl, "%s add %q", cn, a->val);
599
		}
600
 
601
		snprint(cn, sizeof cn, "eq_%d", i);
602
		entry[i].eq = createtext(cs, cn);
603
		chanprint(cs->ctl, "%s image white", cn);
604
		chanprint(cs->ctl, "%s add ' = '", cn);
605
	}
606
 
607
	b_done = createtextbutton(cs, "b_done");
608
	chanprint(cs->ctl, "b_done border 1");
609
	chanprint(cs->ctl, "b_done align center");
610
	chanprint(cs->ctl, "b_done text DONE");
611
	chanprint(cs->ctl, "b_done image green");
612
	chanprint(cs->ctl, "b_done light green");
613
 
614
	/* wire controls for input */
615
	c = chancreate(sizeof(char*), 0);
616
	controlwire(b_done, "event", c);
617
	for(i = 0; i < entries; i++)
618
		if(entry[i].a->type == AttrQuery)
619
			controlwire(entry[i].val, "event", c);
620
 
621
	/* make the controls interactive */
622
	activate(msg);
623
	activate(b_done);
624
	for(i = 0; i < entries; i++){
625
		if(entry[i].a->type != AttrQuery)
626
			continue;
627
		if(entry[i].a->name == nil)
628
			activate(entry[i].name);
629
		activate(entry[i].val);
630
	}
631
 
632
	/* change the display */
633
	r->rt->cs = cs;
634
	unhide();
635
	resizecontrolset(cs);
636
 
637
	return c;
638
}
639
 
640
/*
641
 *  resize the controls for the confirmation window
642
 */
643
static void
644
resizeneedkey(Controlset *cs)
645
{
646
	Rectangle r, mr;
647
	int mid, i, n, lasty;
648
 
649
	/* get usable window rectangle */
650
	if(getwindow(display, Refnone) < 0)
651
		ctlerror("resize failed: %r");
652
	r = insetrect(screen->r, 10);
653
 
654
	/* find largest name */
655
	mid = 0;
656
	for(i = 0; i < entries; i++){
657
		if(entry[i].a->name == nil)
658
			continue;
659
		n = strlen(entry[i].a->name);
660
		if(n > mid)
661
			mid = n;
662
	}
663
	mid = (mid+2) * font->height;
664
 
665
	/* top line is the message */
666
	mr = r;
667
	mr.max.y = mr.min.y + 2*font->height + 2;
668
	chanprint(cs->ctl, "msg rect %R\nmsg show", mr);
669
 
670
	/* one line per attribute */
671
	mr.min.x += 2*font->height;
672
	lasty = mr.max.y;
673
	for(i = 0; i < entries; i++){
674
		r.min.x = mr.min.x;
675
		r.min.y = lasty+2;
676
		r.max.x = r.min.x + mid - 3*stringwidth(font, "=");
677
		r.max.y = r.min.y + font->height;
678
		chanprint(cs->ctl, "name_%d rect %R\nname_%d show", i, r, i);
679
 
680
		r.min.x = r.max.x;
681
		r.max.x = r.min.x + 3*stringwidth(font, "=");
682
		chanprint(cs->ctl, "eq_%d rect %R\neq_%d show", i, r, i);
683
 
684
		r.min.x = r.max.x;
685
		r.max.x = mr.max.x;
686
		if(Dx(r) > 32*font->height)
687
			r.max.x = r.min.x + 32*font->height;
688
		chanprint(cs->ctl, "val_%d rect %R\nval_%d show", i, r, i);
689
		lasty = r.max.y;
690
	}
691
 
692
	/* done button */
693
	mr.min.x -= 2*font->height;
694
	r.min.x = mr.min.x + mid - 3*font->height;
695
	r.min.y = lasty+10;
696
	r.max.x = r.min.x + 6*font->height;
697
	r.max.y = r.min.y + font->height + 2;
698
	chanprint(cs->ctl, "b_done rect %R\nb_done show", r);
699
}
700
 
701
/*
702
 *  free the controls for the confirmation window
703
 */
704
static void
705
teardownneedkey(Request *r)
706
{
707
	Controlset *cs;
708
 
709
	cs = r->rt->cs;
710
	r->rt->cs = nil;
711
	hide();
712
	closecontrolset(cs);
713
	closekmr();
714
 
715
	if(entry != nil)
716
		free(entry);
717
	entry = nil;
718
}
719
 
720
static void
721
needkey(Request *r)
722
{
723
	Channel *c;
724
	char *nam, *val;
725
	int i, n;
726
	int fd;
727
	char *args[3];
728
 
729
	/* set up the controls */
730
	c = setupneedkey(r);
731
	if(c == nil)
732
		goto out;
733
 
734
	/* wait for user to reply */
735
	for(;;){
736
		val = recvp(c);
737
		n = tokenize(val, args, nelem(args));
738
		if(n==3 && strcmp(args[1], "value")==0){	/* user hit 'enter' */
739
			free(val);
740
			break;
741
		}
742
		free(val);
743
	}
744
 
745
	/* get entry values */
746
	for(i = 0; i < entries; i++){
747
		if(entry[i].a->type != AttrQuery)
748
			continue;
749
 
750
		chanprint(r->rt->cs->ctl, "val_%d data", i);
751
		val = recvp(entry[i].val->data);
752
		if(entry[i].a->name == nil){
753
			chanprint(r->rt->cs->ctl, "name_%d data", i);
754
			nam = recvp(entry[i].name->data);
755
			if(nam == nil || *nam == 0){
756
				free(val);
757
				continue;
758
			}
759
			entry[i].a->val = estrdup(val);
760
			free(val);
761
			entry[i].a->name = estrdup(nam);
762
			free(nam);
763
		} else {
764
			if(val != nil){
765
				entry[i].a->val = estrdup(val);
766
				free(val);
767
			}
768
		}
769
		entry[i].a->type = AttrNameval;
770
	}
771
 
772
	/* enter the new key !!!!need to do something in case of error!!!! */
773
	fd = open("/mnt/factotum/ctl", OWRITE);
774
	fprint(fd, "key %A", r->a);
775
	close(fd);
776
 
777
	teardownneedkey(r);
778
out:
779
	fprint(r->rt->fd, "%A", r->tag);
780
}