xref: /haiku/src/kits/app/Application.cpp (revision 9ecf9d1c1d4888d341a6eac72112c72d1ae3a4cb)
1 /*
2  * Copyright 2001-2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Erik Jaesler (erik@cgsoftware.com)
7  * 		Jerome Duval
8  */
9 
10 
11 #include <new>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 
17 #include <Alert.h>
18 #include <AppFileInfo.h>
19 #include <Application.h>
20 #include <AppMisc.h>
21 #include <MessageRunner.h>
22 #include <Cursor.h>
23 #include <Debug.h>
24 #include <Entry.h>
25 #include <File.h>
26 #include <Locker.h>
27 #include <Path.h>
28 #include <PropertyInfo.h>
29 #include <RegistrarDefs.h>
30 #include <Resources.h>
31 #include <Roster.h>
32 #include <RosterPrivate.h>
33 #include <Window.h>
34 
35 #include <AppServerLink.h>
36 #include <LooperList.h>
37 #include <MenuWindow.h>
38 #include <ObjectLocker.h>
39 #include <PortLink.h>
40 #include <ServerMemoryAllocator.h>
41 #include <ServerProtocol.h>
42 
43 using namespace BPrivate;
44 
45 // Globals ---------------------------------------------------------------------
46 BApplication *be_app = NULL;
47 BMessenger be_app_messenger;
48 
49 BResources *BApplication::sAppResources = NULL;
50 BLocker BApplication::sAppResourcesLock("_app_resources_lock");
51 
52 
53 static property_info sPropertyInfo[] = {
54 	{
55 		"Window",
56 		{},
57 		{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
58 		NULL, 0,
59 		{},
60 		{},
61 		{}
62 	},
63 	{
64 		"Window",
65 		{},
66 		{B_NAME_SPECIFIER},
67 		NULL, 1,
68 		{},
69 		{},
70 		{}
71 	},
72 	{
73 		"Looper",
74 		{},
75 		{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
76 		NULL, 2,
77 		{},
78 		{},
79 		{}
80 	},
81 	{
82 		"Looper",
83 		{},
84 		{B_ID_SPECIFIER},
85 		NULL, 3,
86 		{},
87 		{},
88 		{}
89 	},
90 	{
91 		"Looper",
92 		{},
93 		{B_NAME_SPECIFIER},
94 		NULL, 4,
95 		{},
96 		{},
97 		{}
98 	},
99 	{
100 		"Name",
101 		{B_GET_PROPERTY},
102 		{B_DIRECT_SPECIFIER},
103 		NULL, 5,
104 		{B_STRING_TYPE},
105 		{},
106 		{}
107 	},
108 	{
109 		"Window",
110 		{B_COUNT_PROPERTIES},
111 		{B_DIRECT_SPECIFIER},
112 		NULL, 5,
113 		{B_INT32_TYPE},
114 		{},
115 		{}
116 	},
117 	{
118 		"Loopers",
119 		{B_GET_PROPERTY},
120 		{B_DIRECT_SPECIFIER},
121 		NULL, 5,
122 		{B_MESSENGER_TYPE},
123 		{},
124 		{}
125 	},
126 	{
127 		"Windows",
128 		{B_GET_PROPERTY},
129 		{B_DIRECT_SPECIFIER},
130 		NULL, 5,
131 		{B_MESSENGER_TYPE},
132 		{},
133 		{}
134 	},
135 	{
136 		"Looper",
137 		{B_COUNT_PROPERTIES},
138 		{B_DIRECT_SPECIFIER},
139 		NULL, 5,
140 		{B_INT32_TYPE},
141 		{},
142 		{}
143 	},
144 	{}
145 };
146 
147 // argc/argv
148 extern const int __libc_argc;
149 extern const char * const *__libc_argv;
150 
151 
152 // debugging
153 //#define DBG(x) x
154 #define DBG(x)
155 #define OUT	printf
156 
157 
158 // prototypes of helper functions
159 static const char* looper_name_for(const char *signature);
160 static status_t check_app_signature(const char *signature);
161 static void fill_argv_message(BMessage &message);
162 
163 
164 BApplication::BApplication(const char *signature)
165 	: BLooper(looper_name_for(signature))
166 {
167 	_InitData(signature, true, NULL);
168 }
169 
170 
171 BApplication::BApplication(const char *signature, status_t *_error)
172 	: BLooper(looper_name_for(signature))
173 {
174 	_InitData(signature, true, _error);
175 }
176 
177 
178 BApplication::BApplication(const char *signature, bool initGUI,
179 		status_t *_error)
180 	: BLooper(looper_name_for(signature))
181 {
182 	_InitData(signature, initGUI, _error);
183 }
184 
185 
186 BApplication::BApplication(BMessage *data)
187 	// Note: BeOS calls the private BLooper(int32, port_id, const char *)
188 	// constructor here, test if it's needed
189 	: BLooper(looper_name_for(NULL))
190 {
191 	const char *signature = NULL;
192 	data->FindString("mime_sig", &signature);
193 
194 	_InitData(signature, true, NULL);
195 
196 	bigtime_t pulseRate;
197 	if (data->FindInt64("_pulse", &pulseRate) == B_OK)
198 		SetPulseRate(pulseRate);
199 
200 }
201 
202 
203 BApplication::BApplication(uint32 signature)
204 {
205 }
206 
207 
208 BApplication::BApplication(const BApplication &rhs)
209 {
210 }
211 
212 
213 BApplication::~BApplication()
214 {
215 	Lock();
216 
217 	// tell all loopers(usually windows) to quit. Also, wait for them.
218 	_QuitAllWindows(true);
219 
220 	// unregister from the roster
221 	BRoster::Private().RemoveApp(Team());
222 
223 #ifndef RUN_WITHOUT_APP_SERVER
224 	// tell app_server we're quitting...
225 	BPrivate::AppServerLink link;
226 	link.StartMessage(B_QUIT_REQUESTED);
227 	link.Flush();
228 
229 	delete_port(fServerLink->SenderPort());
230 	delete_port(fServerLink->ReceiverPort());
231 	delete fServerLink;
232 #endif	// RUN_WITHOUT_APP_SERVER
233 
234 	delete fServerAllocator;
235 
236 	// uninitialize be_app, the be_app_messenger is invalidated automatically
237 	be_app = NULL;
238 }
239 
240 
241 BApplication &
242 BApplication::operator=(const BApplication &rhs)
243 {
244 	return *this;
245 }
246 
247 
248 void
249 BApplication::_InitData(const char *signature, bool initGUI, status_t *_error)
250 {
251 	DBG(OUT("BApplication::InitData(`%s', %p)\n", signature, _error));
252 	// check whether there exists already an application
253 	if (be_app)
254 		debugger("2 BApplication objects were created. Only one is allowed.");
255 
256 	fServerLink = new BPrivate::PortLink(-1, -1);
257 	fServerAllocator = NULL;
258 	fInitialWorkspace = 0;
259 	//fDraggedMessage = NULL;
260 	fReadyToRunCalled = false;
261 
262 	// initially, there is no pulse
263 	fPulseRunner = NULL;
264 	fPulseRate = 0;
265 
266 	// check signature
267 	fInitError = check_app_signature(signature);
268 	fAppName = signature;
269 
270 #ifndef RUN_WITHOUT_REGISTRAR
271 	bool isRegistrar = signature
272 		&& !strcasecmp(signature, kRegistrarSignature);
273 	// get team and thread
274 	team_id team = Team();
275 	thread_id thread = BPrivate::main_thread_for(team);
276 #endif
277 
278 	// get app executable ref
279 	entry_ref ref;
280 	if (fInitError == B_OK) {
281 		fInitError = BPrivate::get_app_ref(&ref);
282 		if (fInitError != B_OK) {
283 			DBG(OUT("BApplication::InitData(): Failed to get app ref: %s\n",
284 				strerror(fInitError)));
285 		}
286 	}
287 
288 	// get the BAppFileInfo and extract the information we need
289 	uint32 appFlags = B_REG_DEFAULT_APP_FLAGS;
290 	if (fInitError == B_OK) {
291 		BAppFileInfo fileInfo;
292 		BFile file(&ref, B_READ_ONLY);
293 		fInitError = fileInfo.SetTo(&file);
294 		if (fInitError == B_OK) {
295 			fileInfo.GetAppFlags(&appFlags);
296 			char appFileSignature[B_MIME_TYPE_LENGTH];
297 			// compare the file signature and the supplied signature
298 			if (fileInfo.GetSignature(appFileSignature) == B_OK
299 				&& strcasecmp(appFileSignature, signature) != 0) {
300 				printf("Signature in rsrc doesn't match constructor arg. (%s, %s)\n",
301 					signature, appFileSignature);
302 			}
303 		} else {
304 			DBG(OUT("BApplication::InitData(): Failed to get info from: "
305 				"BAppFileInfo: %s\n", strerror(fInitError)));
306 		}
307 	}
308 
309 #ifndef RUN_WITHOUT_REGISTRAR
310 	// check whether be_roster is valid
311 	if (fInitError == B_OK && !isRegistrar
312 		&& !BRoster::Private().IsMessengerValid(false)) {
313 		printf("FATAL: be_roster is not valid. Is the registrar running?\n");
314 		fInitError = B_NO_INIT;
315 	}
316 
317 	// check whether or not we are pre-registered
318 	bool preRegistered = false;
319 	app_info appInfo;
320 	if (fInitError == B_OK && !isRegistrar) {
321 		preRegistered = BRoster::Private().IsAppPreRegistered(&ref, team,
322 							&appInfo);
323 	}
324 	if (preRegistered) {
325 		// we are pre-registered => the app info has been filled in
326 		// Check whether we need to replace the looper port with a port
327 		// created by the roster.
328 		if (appInfo.port >= 0 && appInfo.port != fMsgPort) {
329 			delete_port(fMsgPort);
330 			fMsgPort = appInfo.port;
331 		} else
332 			appInfo.port = fMsgPort;
333 		// check the signature and correct it, if necessary, also the case
334 		if (strcmp(appInfo.signature, fAppName))
335 			BRoster::Private().SetSignature(team, fAppName);
336 		// complete the registration
337 		fInitError = BRoster::Private().CompleteRegistration(team, thread,
338 						appInfo.port);
339 	} else if (fInitError == B_OK) {
340 		// not pre-registered -- try to register the application
341 		team_id otherTeam = -1;
342 		// the registrar must not register
343 		if (!isRegistrar) {
344 			fInitError = BRoster::Private().AddApplication(signature, &ref,
345 				appFlags, team, thread, fMsgPort, true, NULL, &otherTeam);
346 			if (fInitError != B_OK) {
347 				DBG(OUT("BApplication::InitData(): Failed to add app: %s\n",
348 					strerror(fInitError)));
349 			}
350 		}
351 		if (fInitError == B_ALREADY_RUNNING) {
352 			// An instance is already running and we asked for
353 			// single/exclusive launch. Send our argv to the running app.
354 			// Do that only, if the app is NOT B_ARGV_ONLY.
355 			if (otherTeam >= 0 && __libc_argc > 1) {
356 				app_info otherAppInfo;
357 				if (be_roster->GetRunningAppInfo(otherTeam, &otherAppInfo) == B_OK
358 					&& !(otherAppInfo.flags & B_ARGV_ONLY)) {
359 					// create an B_ARGV_RECEIVED message
360 					BMessage argvMessage(B_ARGV_RECEIVED);
361 					fill_argv_message(argvMessage);
362 
363 					// replace the first argv string with the path of the
364 					// other application
365 					BPath path;
366 					if (path.SetTo(&otherAppInfo.ref) == B_OK)
367 						argvMessage.ReplaceString("argv", 0, path.Path());
368 
369 					// send the message
370 					BMessenger(NULL, otherTeam).SendMessage(&argvMessage);
371 				}
372 			}
373 		} else if (fInitError == B_OK) {
374 			// the registrations was successful
375 			// Create a B_ARGV_RECEIVED message and send it to ourselves.
376 			// Do that even, if we are B_ARGV_ONLY.
377 			// TODO: When BLooper::AddMessage() is done, use that instead of
378 			// PostMessage().
379 
380 			DBG(OUT("info: BApplication sucessfully registered.\n"));
381 
382 			if (__libc_argc > 1) {
383 				BMessage argvMessage(B_ARGV_RECEIVED);
384 				fill_argv_message(argvMessage);
385 				PostMessage(&argvMessage, this);
386 			}
387 			// send a B_READY_TO_RUN message as well
388 			PostMessage(B_READY_TO_RUN, this);
389 		} else if (fInitError > B_ERRORS_END) {
390 			// Registrar internal errors shouldn't fall into the user's hands.
391 			fInitError = B_ERROR;
392 		}
393 	}
394 #else
395 	// We need to have ReadyToRun called even when we're not using the registrar
396 	PostMessage(B_READY_TO_RUN, this);
397 #endif	// ifndef RUN_WITHOUT_REGISTRAR
398 
399 	// TODO: Not completely sure about the order, but this should be close.
400 
401 	// init be_app and be_app_messenger
402 	if (fInitError == B_OK) {
403 		be_app = this;
404 		be_app_messenger = BMessenger(NULL, this);
405 	}
406 
407 	// set the BHandler's name
408 	if (fInitError == B_OK)
409 		SetName(ref.name);
410 
411 	// create meta MIME
412 	if (fInitError == B_OK) {
413 		BPath path;
414 		if (path.SetTo(&ref) == B_OK)
415 			create_app_meta_mime(path.Path(), false, true, false);
416 	}
417 
418 #ifndef RUN_WITHOUT_APP_SERVER
419 	// app server connection and IK initialization
420 	if (fInitError == B_OK && initGUI)
421 		fInitError = _InitGUIContext();
422 #endif	// RUN_WITHOUT_APP_SERVER
423 
424 	// Return the error or exit, if there was an error and no error variable
425 	// has been supplied.
426 	if (_error) {
427 		*_error = fInitError;
428 	} else if (fInitError != B_OK) {
429 		DBG(OUT("BApplication::InitData() failed: %s\n", strerror(fInitError)));
430 		exit(0);
431 	}
432 DBG(OUT("BApplication::InitData() done\n"));
433 }
434 
435 
436 BArchivable *
437 BApplication::Instantiate(BMessage *data)
438 {
439 	if (validate_instantiation(data, "BApplication"))
440 		return new BApplication(data);
441 
442 	return NULL;
443 }
444 
445 
446 status_t
447 BApplication::Archive(BMessage *data, bool deep) const
448 {
449 	status_t status = BLooper::Archive(data, deep);
450 	if (status < B_OK)
451 		return status;
452 
453 	app_info info;
454 	status = GetAppInfo(&info);
455 	if (status < B_OK)
456 		return status;
457 
458 	status = data->AddString("mime_sig", info.signature);
459 	if (status < B_OK)
460 		return status;
461 
462 	return data->AddInt64("_pulse", fPulseRate);
463 }
464 
465 
466 status_t
467 BApplication::InitCheck() const
468 {
469 	return fInitError;
470 }
471 
472 
473 thread_id
474 BApplication::Run()
475 {
476 	if (fInitError != B_OK)
477 		return fInitError;
478 
479 	AssertLocked();
480 
481 	if (fRunCalled)
482 		debugger("BApplication::Run was already called. Can only be called once.");
483 
484 	// Note: We need a local variable too (for the return value), since
485 	// fTaskID is cleared by Quit().
486 // ToDo: actually, it's not clobbered there?!
487 	thread_id thread = fTaskID = find_thread(NULL);
488 
489 	fRunCalled = true;
490 
491 	task_looper();
492 
493 	delete fPulseRunner;
494 	return thread;
495 }
496 
497 
498 void
499 BApplication::Quit()
500 {
501 	bool unlock = false;
502 	if (!IsLocked()) {
503 		const char *name = Name();
504 		if (!name)
505 			name = "no-name";
506 		printf("ERROR - you must Lock the application object before calling "
507 			   "Quit(), team=%ld, looper=%s\n", Team(), name);
508 		unlock = true;
509 		if (!Lock())
510 			return;
511 	}
512 	// Delete the object, if not running only.
513 	if (!fRunCalled) {
514 		delete this;
515 	} else if (find_thread(NULL) != fTaskID) {
516 // ToDo: why shouldn't we set fTerminating to true directly in this case?
517 		// We are not the looper thread.
518 		// We push a _QUIT_ into the queue.
519 		// TODO: When BLooper::AddMessage() is done, use that instead of
520 		// PostMessage()??? This would overtake messages that are still at
521 		// the port.
522 		// NOTE: We must not unlock here -- otherwise we had to re-lock, which
523 		// may not work. This is bad, since, if the port is full, it
524 		// won't get emptier, as the looper thread needs to lock the object
525 		// before dispatching messages.
526 		while (PostMessage(_QUIT_, this) == B_WOULD_BLOCK)
527 			snooze(10000);
528 	} else {
529 		// We are the looper thread.
530 		// Just set fTerminating to true which makes us fall through the
531 		// message dispatching loop and return from Run().
532 		fTerminating = true;
533 	}
534 	// If we had to lock the object, unlock now.
535 	if (unlock)
536 		Unlock();
537 }
538 
539 
540 bool
541 BApplication::QuitRequested()
542 {
543 	return _QuitAllWindows(false);
544 }
545 
546 
547 void
548 BApplication::Pulse()
549 {
550 	// supposed to be implemented by subclasses
551 }
552 
553 
554 void
555 BApplication::ReadyToRun()
556 {
557 	// supposed to be implemented by subclasses
558 }
559 
560 
561 void
562 BApplication::MessageReceived(BMessage *message)
563 {
564 	switch (message->what) {
565 		case B_COUNT_PROPERTIES:
566 		case B_GET_PROPERTY:
567 		case B_SET_PROPERTY:
568 		{
569 			int32 index;
570 			BMessage specifier;
571 			int32 what;
572 			const char *property = NULL;
573 			if (message->GetCurrentSpecifier(&index, &specifier, &what, &property) < B_OK
574 				|| !ScriptReceived(message, index, &specifier, what, property))
575 				BLooper::MessageReceived(message);
576 			break;
577 		}
578 
579 		// Bebook says: B_SILENT_RELAUNCH
580 		// Sent to a single-launch application when it's activated by being launched
581 		// (for example, if the user double-clicks its icon in Tracker).
582 		case B_SILENT_RELAUNCH:
583 			be_roster->ActivateApp(Team());
584 			// supposed to fall through
585 		default:
586 			BLooper::MessageReceived(message);
587 			break;
588 	}
589 }
590 
591 
592 void
593 BApplication::ArgvReceived(int32 argc, char **argv)
594 {
595 	// supposed to be implemented by subclasses
596 }
597 
598 
599 void
600 BApplication::AppActivated(bool active)
601 {
602 	// supposed to be implemented by subclasses
603 }
604 
605 
606 void
607 BApplication::RefsReceived(BMessage *message)
608 {
609 	// supposed to be implemented by subclasses
610 }
611 
612 
613 void
614 BApplication::AboutRequested()
615 {
616 	thread_info info;
617 	if (get_thread_info(Thread(), &info) == B_OK) {
618 		BAlert *alert = new BAlert("_about_", info.name, "OK");
619 		alert->Go(NULL);
620 	}
621 }
622 
623 
624 BHandler *
625 BApplication::ResolveSpecifier(BMessage *message, int32 index,
626         BMessage *specifier, int32 what, const char *property)
627 {
628 	BPropertyInfo propInfo(sPropertyInfo);
629 	status_t err = B_OK;
630 	uint32 data;
631 
632 	if (propInfo.FindMatch(message, 0, specifier, what, property, &data) >=0) {
633 		switch (data) {
634 			case 0: {
635 				int32 ind = -1;
636 				err = specifier->FindInt32("index", &ind);
637 				if (err != B_OK)
638 					break;
639 				if (what == B_REVERSE_INDEX_SPECIFIER)
640 					ind = CountWindows() - ind;
641 				err = B_BAD_INDEX;
642 				BWindow *win = WindowAt(ind);
643 				if (win) {
644 					if (index <= 0 && message->what == B_GET_PROPERTY)
645 						return this;
646 					message->PopSpecifier();
647 					BMessenger(win).SendMessage(message);
648 					return NULL;
649 				}
650 				break;
651 			}
652 			case 1: {
653 				const char *name;
654 				err = specifier->FindString("name", &name);
655 				if (err != B_OK)
656 					break;
657 				err = B_NAME_NOT_FOUND;
658 				for (int32 i=0; i<CountWindows(); i++) {
659 					BWindow *win = WindowAt(i);
660 					if (win && win->Name() && strlen(win->Name()) == strlen(name)
661 						&& !strcmp(win->Name(), name)) {
662 							if (index <= 0 && message->what == B_GET_PROPERTY)
663 								return this;
664 							message->PopSpecifier();
665 							BMessenger(win).SendMessage(message);
666 							return NULL;
667 					}
668 				}
669 				break;
670 			}
671 			case 2: {
672 				int32 ind = -1;
673 				err = specifier->FindInt32("index", &ind);
674 				if (err != B_OK)
675 					break;
676 				if (what == B_REVERSE_INDEX_SPECIFIER)
677 					ind = CountLoopers() - ind;
678 				err = B_BAD_INDEX;
679 				BLooper *looper = LooperAt(ind);
680 				if (looper) {
681 					if (index <= 0)
682 						return this;
683 					message->PopSpecifier();
684 					BMessenger(looper).SendMessage(message);
685 					return NULL;
686 				}
687 				break;
688 			}
689 			case 3:
690 				//if (index == 0)
691 				//	return this;
692 
693 				break;
694 			case 4: {
695 				const char *name;
696 				err = specifier->FindString("name", &name);
697 				if (err != B_OK)
698 					break;
699 				err = B_NAME_NOT_FOUND;
700 				for (int32 i=0; i<CountLoopers(); i++) {
701 					BLooper *looper = LooperAt(i);
702 					if (looper && looper->Name() && strlen(looper->Name()) == strlen(name)
703 						&& !strcmp(looper->Name(), name)) {
704 						if (index <= 0)
705 							return this;
706 						message->PopSpecifier();
707 						BMessenger(looper).SendMessage(message);
708 						return NULL;
709 					}
710 				}
711 				break;
712 			}
713 			case 5:
714 				return this;
715 		}
716 	} else {
717 			return BLooper::ResolveSpecifier(message, index, specifier, what,
718 				property);
719 	}
720 
721 	BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
722 	reply.AddInt32("error", err);
723 	reply.AddString("message", strerror(err));
724 	message->SendReply(&reply);
725 
726 	return NULL;
727 
728 }
729 
730 
731 void
732 BApplication::ShowCursor()
733 {
734 	BPrivate::AppServerLink link;
735 	link.StartMessage(AS_SHOW_CURSOR);
736 	link.Flush();
737 }
738 
739 
740 void
741 BApplication::HideCursor()
742 {
743 	BPrivate::AppServerLink link;
744 	link.StartMessage(AS_HIDE_CURSOR);
745 	link.Flush();
746 }
747 
748 
749 void
750 BApplication::ObscureCursor()
751 {
752 	BPrivate::AppServerLink link;
753 	link.StartMessage(AS_OBSCURE_CURSOR);
754 	link.Flush();
755 }
756 
757 
758 bool
759 BApplication::IsCursorHidden() const
760 {
761 	BPrivate::AppServerLink link;
762 	int32 status = B_ERROR;
763 	link.StartMessage(AS_QUERY_CURSOR_HIDDEN);
764 	link.FlushWithReply(status);
765 
766 	return status == B_OK;
767 }
768 
769 
770 void
771 BApplication::SetCursor(const void *cursorData)
772 {
773 	BCursor cursor(cursorData);
774 	SetCursor(&cursor, true);
775 		// forces the cursor to be sync'ed
776 }
777 
778 
779 void
780 BApplication::SetCursor(const BCursor *cursor, bool sync)
781 {
782 	BPrivate::AppServerLink link;
783 	link.StartMessage(AS_SET_CURSOR);
784 	link.Attach<bool>(sync);
785 	link.Attach<int32>(cursor->fServerToken);
786 
787 	if (sync) {
788 		int32 code;
789 		link.FlushWithReply(code);
790 	} else
791 		link.Flush();
792 }
793 
794 
795 int32
796 BApplication::CountWindows() const
797 {
798 	return _CountWindows(false);
799 		// we're ignoring menu windows
800 }
801 
802 
803 BWindow *
804 BApplication::WindowAt(int32 index) const
805 {
806 	return _WindowAt(index, false);
807 		// we're ignoring menu windows
808 }
809 
810 
811 int32
812 BApplication::CountLoopers() const
813 {
814 	BObjectLocker<BLooperList> ListLock(gLooperList);
815 	if (ListLock.IsLocked())
816 		return gLooperList.CountLoopers();
817 
818 	// Some bad, non-specific thing has happened
819 	return B_ERROR;
820 }
821 
822 
823 BLooper *
824 BApplication::LooperAt(int32 index) const
825 {
826 	BLooper *looper = NULL;
827 	BObjectLocker<BLooperList> listLock(gLooperList);
828 	if (listLock.IsLocked())
829 		looper = gLooperList.LooperAt(index);
830 
831 	return looper;
832 }
833 
834 
835 bool
836 BApplication::IsLaunching() const
837 {
838 	return !fReadyToRunCalled;
839 }
840 
841 
842 status_t
843 BApplication::GetAppInfo(app_info *info) const
844 {
845 	return be_roster->GetRunningAppInfo(be_app->Team(), info);
846 }
847 
848 
849 BResources *
850 BApplication::AppResources()
851 {
852 	BObjectLocker<BLocker> lock(sAppResourcesLock);
853 
854 	// BApplication caches its resources, so check
855 	// if it already happened.
856 	if (sAppResources != NULL)
857 		return sAppResources;
858 
859 	entry_ref ref;
860 	bool found = false;
861 
862 	// App is already running. Get its entry ref with
863 	// GetRunningAppInfo()
864 	app_info appInfo;
865 	if (be_app && be_roster->GetRunningAppInfo(be_app->Team(), &appInfo) == B_OK) {
866 		ref = appInfo.ref;
867 		found = true;
868 	} else {
869 		// Run() hasn't been called yet
870 		found = BPrivate::get_app_ref(&ref) == B_OK;
871 	}
872 
873 	if (found) {
874 		BFile file(&ref, B_READ_ONLY);
875 		if (file.InitCheck() == B_OK) {
876 			BResources *resources = new BResources();
877 			if (resources->SetTo(&file, false) < B_OK)
878 				delete resources;
879 			else
880 				sAppResources = resources;
881 		}
882 	}
883 
884 	return sAppResources;
885 }
886 
887 
888 void
889 BApplication::DispatchMessage(BMessage *message, BHandler *handler)
890 {
891 	if (handler != this) {
892 		// it's not ours to dispatch
893 		BLooper::DispatchMessage(message, handler);
894 		return;
895 	}
896 
897 	switch (message->what) {
898 		case B_ARGV_RECEIVED:
899 			_ArgvReceived(message);
900 			break;
901 
902 		case B_REFS_RECEIVED:
903 		{
904 			// this adds the refs that are part of this message to the recent
905 			// lists, but only folders and documents are handled here
906 			entry_ref ref;
907 			int32 i = 0;
908 			while (message->FindRef("refs", i++, &ref) == B_OK) {
909 				BEntry entry(&ref, true);
910 				if (entry.InitCheck() != B_OK)
911 					continue;
912 
913 				if (entry.IsDirectory())
914 					BRoster().AddToRecentFolders(&ref);
915 				else {
916 					// filter out applications, we only want to have documents
917 					// in the recent files list
918 					BNode node(&entry);
919 					BNodeInfo info(&node);
920 
921 					char mimeType[B_MIME_TYPE_LENGTH];
922 					if (info.GetType(mimeType) != B_OK
923 						|| strcasecmp(mimeType, B_APP_MIME_TYPE))
924 						BRoster().AddToRecentDocuments(&ref);
925 				}
926 			}
927 
928 			RefsReceived(message);
929 			break;
930 		}
931 
932 		case B_READY_TO_RUN:
933 			if (!fReadyToRunCalled) {
934 				ReadyToRun();
935 				fReadyToRunCalled = true;
936 			}
937 			break;
938 
939 		case B_ABOUT_REQUESTED:
940 			AboutRequested();
941 			break;
942 
943 		case B_PULSE:
944 			Pulse();
945 			break;
946 
947 		case B_APP_ACTIVATED:
948 		{
949 			bool active = false;
950 			message->FindBool("active", &active);
951 			AppActivated(active);
952 			break;
953 		}
954 
955 		case _SHOW_DRAG_HANDLES_:
956 		{
957 			bool visible = false;
958 			message->FindBool("visible", &visible);
959 			// TODO: Call the registrar or whoever is responsible for this
960 			break;
961 		}
962 
963 		// TODO: Handle these as well
964 		case _DISPOSE_DRAG_:
965 		case _PING_:
966 			puts("not yet handled message:");
967 			DBG(message->PrintToStream());
968 			break;
969 
970 		default:
971 			BLooper::DispatchMessage(message, handler);
972 			break;
973 	}
974 }
975 
976 
977 void
978 BApplication::SetPulseRate(bigtime_t rate)
979 {
980 	if (rate < 0)
981 		rate = 0;
982 
983 	// BeBook states that we have only 100,000 microseconds granularity
984 	rate -= rate % 100000;
985 
986 	if (!Lock())
987 		return;
988 
989 	if (rate != 0) {
990 		// reset existing pulse runner, or create new one
991 		if (fPulseRunner == NULL) {
992 			BMessage pulse(B_PULSE);
993 			fPulseRunner = new BMessageRunner(be_app_messenger, &pulse, rate);
994 		} else
995 			fPulseRunner->SetInterval(rate);
996 	} else {
997 		// turn off pulse messages
998 		delete fPulseRunner;
999 		fPulseRunner = NULL;
1000 	}
1001 
1002 	fPulseRate = rate;
1003 	Unlock();
1004 }
1005 
1006 
1007 status_t
1008 BApplication::GetSupportedSuites(BMessage *data)
1009 {
1010 	if (!data)
1011 		return B_BAD_VALUE;
1012 
1013 	status_t status = data->AddString("suites", "suite/vnd.Be-application");
1014 	if (status == B_OK) {
1015 		BPropertyInfo propertyInfo(sPropertyInfo);
1016 		status = data->AddFlat("messages", &propertyInfo);
1017 		if (status == B_OK)
1018 			status = BLooper::GetSupportedSuites(data);
1019 	}
1020 
1021 	return status;
1022 }
1023 
1024 
1025 status_t
1026 BApplication::Perform(perform_code d, void *arg)
1027 {
1028 	return BLooper::Perform(d, arg);
1029 }
1030 
1031 
1032 void BApplication::_ReservedApplication1() {}
1033 void BApplication::_ReservedApplication2() {}
1034 void BApplication::_ReservedApplication3() {}
1035 void BApplication::_ReservedApplication4() {}
1036 void BApplication::_ReservedApplication5() {}
1037 void BApplication::_ReservedApplication6() {}
1038 void BApplication::_ReservedApplication7() {}
1039 void BApplication::_ReservedApplication8() {}
1040 
1041 
1042 bool
1043 BApplication::ScriptReceived(BMessage *message, int32 index,
1044 	BMessage *specifier, int32 what, const char *property)
1045 {
1046 	BMessage reply(B_REPLY);
1047 	status_t err = B_BAD_SCRIPT_SYNTAX;
1048 
1049 	switch (message->what) {
1050 		case B_GET_PROPERTY:
1051 			if (strcmp("Loopers", property) == 0) {
1052 				int32 count = CountLoopers();
1053 				err = B_OK;
1054 				for (int32 i=0; err == B_OK && i<count; i++) {
1055 					BMessenger messenger(LooperAt(i));
1056 					err = reply.AddMessenger("result", messenger);
1057 				}
1058 			} else if (strcmp("Windows", property) == 0) {
1059 				int32 count = CountWindows();
1060 				err = B_OK;
1061 				for (int32 i=0; err == B_OK && i<count; i++) {
1062 					BMessenger messenger(WindowAt(i));
1063 					err = reply.AddMessenger("result", messenger);
1064 				}
1065 			} else if (strcmp("Window", property) == 0) {
1066 				switch (what) {
1067 				case B_INDEX_SPECIFIER:
1068 				case B_REVERSE_INDEX_SPECIFIER: {
1069 					int32 ind = -1;
1070 					err = specifier->FindInt32("index", &ind);
1071 					if (err != B_OK)
1072 						break;
1073 					if (what == B_REVERSE_INDEX_SPECIFIER)
1074 						ind = CountWindows() - ind;
1075 					err = B_BAD_INDEX;
1076 					BWindow *win = WindowAt(ind);
1077 					if (!win)
1078 						break;
1079 					BMessenger messenger(win);
1080 					err = reply.AddMessenger("result", messenger);
1081 					break;
1082 				}
1083 				case B_NAME_SPECIFIER: {
1084 					const char *name;
1085 					err = specifier->FindString("name", &name);
1086 					if (err != B_OK)
1087 						break;
1088 					err = B_NAME_NOT_FOUND;
1089 					for (int32 i=0; i<CountWindows(); i++) {
1090 						BWindow *win = WindowAt(i);
1091 						if (win && win->Name() && strlen(win->Name()) == strlen(name)
1092 							&& !strcmp(win->Name(), name)) {
1093 							BMessenger messenger(win);
1094 							err = reply.AddMessenger("result", messenger);
1095 							break;
1096 						}
1097 					}
1098 				}
1099 				}
1100 			} else if (strcmp("Looper", property) == 0) {
1101 				switch (what) {
1102 				case B_INDEX_SPECIFIER:
1103 				case B_REVERSE_INDEX_SPECIFIER: {
1104 					int32 ind = -1;
1105 					err = specifier->FindInt32("index", &ind);
1106 					if (err != B_OK)
1107 						break;
1108 					if (what == B_REVERSE_INDEX_SPECIFIER)
1109 						ind = CountLoopers() - ind;
1110 					err = B_BAD_INDEX;
1111 					BLooper *looper = LooperAt(ind);
1112 					if (!looper)
1113 						break;
1114 					BMessenger messenger(looper);
1115 					err = reply.AddMessenger("result", messenger);
1116 					break;
1117 				}
1118 				case B_NAME_SPECIFIER: {
1119 					const char *name;
1120 					err = specifier->FindString("name", &name);
1121 					if (err != B_OK)
1122 						break;
1123 					err = B_NAME_NOT_FOUND;
1124 					for (int32 i=0; i<CountLoopers(); i++) {
1125 						BLooper *looper = LooperAt(i);
1126 						if (looper && looper->Name() && strlen(looper->Name()) == strlen(name)
1127 							&& !strcmp(looper->Name(), name)) {
1128 							BMessenger messenger(looper);
1129 							err = reply.AddMessenger("result", messenger);
1130 							break;
1131 						}
1132 					}
1133 					break;
1134 				}
1135 				case B_ID_SPECIFIER: {
1136 					// TODO
1137 					break;
1138 				}
1139 				}
1140 			} else if (strcmp("Name", property) == 0) {
1141 				err = reply.AddString("result", Name());
1142 			}
1143 			break;
1144 		case B_COUNT_PROPERTIES:
1145 			if (strcmp("Looper", property) == 0) {
1146 				err = reply.AddInt32("result", CountLoopers());
1147 			} else if (strcmp("Window", property) == 0) {
1148 				err = reply.AddInt32("result", CountWindows());
1149 			}
1150 			break;
1151 	}
1152 	if (err == B_BAD_SCRIPT_SYNTAX) {
1153 		return false;
1154 	}
1155 	if (err < B_OK) {
1156                 reply.what = B_ERROR;
1157                 reply.AddString("message", strerror(err));
1158         }
1159 	reply.AddInt32("error", err);
1160 	message->SendReply(&reply);
1161 	return true;
1162 }
1163 
1164 
1165 void
1166 BApplication::BeginRectTracking(BRect rect, bool trackWhole)
1167 {
1168 	BPrivate::AppServerLink link;
1169 	link.StartMessage(AS_BEGIN_RECT_TRACKING);
1170 	link.Attach<BRect>(rect);
1171 	link.Attach<int32>(trackWhole);
1172 	link.Flush();
1173 }
1174 
1175 
1176 void
1177 BApplication::EndRectTracking()
1178 {
1179 	BPrivate::AppServerLink link;
1180 	link.StartMessage(AS_END_RECT_TRACKING);
1181 	link.Flush();
1182 }
1183 
1184 
1185 status_t
1186 BApplication::_SetupServerAllocator()
1187 {
1188 	fServerAllocator = new (std::nothrow) BPrivate::ServerMemoryAllocator();
1189 	if (fServerAllocator == NULL)
1190 		return B_NO_MEMORY;
1191 
1192 	return fServerAllocator->InitCheck();
1193 }
1194 
1195 
1196 status_t
1197 BApplication::_InitGUIContext()
1198 {
1199 	// An app_server connection is necessary for a lot of stuff, so get that first.
1200 	status_t error = _ConnectToServer();
1201 	if (error != B_OK)
1202 		return error;
1203 
1204 	// Initialize the IK after we have set be_app because of a construction of a
1205 	// AppServerLink (which depends on be_app) nested inside the call to get_menu_info.
1206 	error = _init_interface_kit_();
1207 	if (error != B_OK)
1208 		return error;
1209 
1210 	// create global system cursors
1211 	B_CURSOR_SYSTEM_DEFAULT = new BCursor(B_HAND_CURSOR);
1212 	B_CURSOR_I_BEAM = new BCursor(B_I_BEAM_CURSOR);
1213 
1214 	// TODO: would be nice to get the workspace at launch time from the registrar
1215 	fInitialWorkspace = current_workspace();
1216 
1217 	return B_OK;
1218 }
1219 
1220 
1221 status_t
1222 BApplication::_ConnectToServer()
1223 {
1224 	port_id serverPort = find_port(SERVER_PORT_NAME);
1225 	if (serverPort < B_OK)
1226 		return serverPort;
1227 
1228 	// Create the port so that the app_server knows where to send messages
1229 	port_id clientPort = create_port(100, "a<app_server");
1230 	if (clientPort < B_OK)
1231 		return clientPort;
1232 
1233 	// We can't use AppServerLink because be_app == NULL
1234 	fServerLink->SetTo(serverPort, clientPort);
1235 
1236 	fServerLink->StartMessage(AS_GET_DESKTOP);
1237 	fServerLink->Attach<port_id>(clientPort);
1238 	fServerLink->Attach<int32>(getuid());
1239 
1240 	int32 code;
1241 	if (fServerLink->FlushWithReply(code) != B_OK || code != B_OK) {
1242 		fServerLink->SetSenderPort(-1);
1243 		return B_ERROR;
1244 	}
1245 
1246 	// we talk to the desktop to create our application
1247 	fServerLink->Read<port_id>(&serverPort);
1248 	fServerLink->SetSenderPort(serverPort);
1249 
1250 	// AS_CREATE_APP:
1251 	//
1252 	// Attach data:
1253 	// 1) port_id - receiver port of a regular app
1254 	// 2) port_id - looper port for this BApplication
1255 	// 3) team_id - team identification field
1256 	// 4) int32 - handler ID token of the app
1257 	// 5) char * - signature of the regular app
1258 
1259 	fServerLink->StartMessage(AS_CREATE_APP);
1260 	fServerLink->Attach<port_id>(clientPort);
1261 	fServerLink->Attach<port_id>(_get_looper_port_(this));
1262 	fServerLink->Attach<team_id>(Team());
1263 	fServerLink->Attach<int32>(_get_object_token_(this));
1264 	fServerLink->AttachString(fAppName);
1265 
1266 	area_id sharedReadOnlyArea;
1267 
1268 	if (fServerLink->FlushWithReply(code) == B_OK
1269 		&& code == B_OK) {
1270 		// We don't need to contact the main app_server anymore
1271 		// directly; we now talk to our server alter ego only.
1272 		fServerLink->Read<port_id>(&serverPort);
1273 		fServerLink->Read<area_id>(&sharedReadOnlyArea);
1274 	} else {
1275 		fServerLink->SetSenderPort(-1);
1276 		debugger("BApplication: couldn't obtain new app_server comm port");
1277 		return B_ERROR;
1278 	}
1279 
1280 	fServerLink->SetSenderPort(serverPort);
1281 
1282 	status_t status = _SetupServerAllocator();
1283 	if (status != B_OK)
1284 		return status;
1285 
1286 	area_id area;
1287 	uint8* base;
1288 	status = fServerAllocator->AddArea(sharedReadOnlyArea, area, base, true);
1289 	if (status < B_OK)
1290 		return status;
1291 
1292 	fServerReadOnlyMemory = base;
1293 	return B_OK;
1294 }
1295 
1296 
1297 #if 0
1298 void
1299 BApplication::send_drag(BMessage *message, int32 vs_token, BPoint offset,
1300 	BRect dragRect, BHandler *replyTo)
1301 {
1302 	// TODO: implement
1303 }
1304 
1305 
1306 void
1307 BApplication::send_drag(BMessage *message, int32 vs_token, BPoint offset,
1308 	int32 bitmapToken, drawing_mode dragMode, BHandler *replyTo)
1309 {
1310 	// TODO: implement
1311 }
1312 
1313 
1314 void
1315 BApplication::write_drag(_BSession_ *session, BMessage *message)
1316 {
1317 	// TODO: implement
1318 }
1319 #endif
1320 
1321 bool
1322 BApplication::_WindowQuitLoop(bool quitFilePanels, bool force)
1323 {
1324 	BList looperList;
1325 	{
1326 		BObjectLocker<BLooperList> listLock(gLooperList);
1327 		if (listLock.IsLocked()) {
1328 			gLooperList.GetLooperList(&looperList);
1329 
1330 			// Filter the list: We replace the BLooper pointers by BWindow
1331 			// pointers (dynamic_cast<>() on unlocked loopers is only safe as
1332 			// long as the looper list is locked!). Furthermore we filter out
1333 			// windows, that have not been run yet -- those belong to their
1334 			// creator yet (which also has a lock) and we must not try to
1335 			// delete them.
1336 			int32 count = looperList.CountItems();
1337 			for (int32 i = 0; i < count; i++) {
1338 				BWindow *window
1339 					= dynamic_cast<BWindow*>((BLooper*)looperList.ItemAt(i));
1340 				if (window && window->Thread() < 0)
1341 					window = NULL;
1342 				looperList.ReplaceItem(i, window);
1343 			}
1344 		}
1345 	}
1346 
1347 	for (int32 i = looperList.CountItems(); i-- > 0; ) {
1348 		BWindow *window = (BWindow*)looperList.ItemAt(i);
1349 
1350 		// don't quit file panels if we haven't been asked for it
1351 		if (window == NULL || (!quitFilePanels && window->IsFilePanel()))
1352 			continue;
1353 
1354 		if (window->Lock()) {
1355 			if (!force && !window->QuitRequested()
1356 				&& !(quitFilePanels && window->IsFilePanel())) {
1357 				// the window does not want to quit, so we don't either
1358 				window->Unlock();
1359 				return false;
1360 			}
1361 
1362 			// Re-lock, just to make sure that the user hasn't done nasty
1363 			// things in QuitRequested(). Quit() unlocks fully, thus
1364 			// double-locking is harmless.
1365 			if (window->Lock())
1366 				window->Quit();
1367 		}
1368 	}
1369 	return true;
1370 }
1371 
1372 
1373 bool
1374 BApplication::_QuitAllWindows(bool force)
1375 {
1376 	AssertLocked();
1377 
1378 	// We need to unlock here because BWindow::QuitRequested() must be
1379 	// allowed to lock the application - which would cause a deadlock
1380 	Unlock();
1381 
1382 	bool quit = _WindowQuitLoop(false, force);
1383 	if (!quit)
1384 		quit = _WindowQuitLoop(true, force);
1385 
1386 	Lock();
1387 
1388 	return quit;
1389 }
1390 
1391 
1392 void
1393 BApplication::_ArgvReceived(BMessage *message)
1394 {
1395 	ASSERT(message != NULL);
1396 
1397 	// build the argv vector
1398 	status_t error = B_OK;
1399 	int32 argc = 0;
1400 	char **argv = NULL;
1401 	if (message->FindInt32("argc", &argc) == B_OK && argc > 0) {
1402 		// allocate a NULL terminated array
1403 		argv = new char*[argc + 1];
1404 		argv[argc] = NULL;
1405 
1406 		// copy the arguments
1407 		for (int32 i = 0; error == B_OK && i < argc; i++) {
1408 			const char *arg = NULL;
1409 			error = message->FindString("argv", i, &arg);
1410 			if (error == B_OK && arg) {
1411 				argv[i] = strdup(arg);
1412 				if (argv[i] == NULL)
1413 					error = B_NO_MEMORY;
1414 			}
1415 		}
1416 	}
1417 
1418 	// call the hook
1419 	if (error == B_OK && argc > 0)
1420 		ArgvReceived(argc, argv);
1421 
1422 	// cleanup
1423 	if (argv) {
1424 		for (int32 i = 0; i < argc; i++)
1425 			free(argv[i]);
1426 		delete[] argv;
1427 	}
1428 }
1429 
1430 
1431 uint32
1432 BApplication::InitialWorkspace()
1433 {
1434 	return fInitialWorkspace;
1435 }
1436 
1437 
1438 int32
1439 BApplication::_CountWindows(bool includeMenus) const
1440 {
1441 	int32 count = 0;
1442 	BList windowList;
1443 	if (_GetWindowList(&windowList, includeMenus) == B_OK)
1444 		count = windowList.CountItems();
1445 
1446 	return count;
1447 }
1448 
1449 
1450 BWindow *
1451 BApplication::_WindowAt(uint32 index, bool includeMenus) const
1452 {
1453 	BList windowList;
1454 	BWindow *window = NULL;
1455 	if (_GetWindowList(&windowList, includeMenus) == B_OK) {
1456 		if ((int32)index < windowList.CountItems())
1457 			window = static_cast<BWindow *>(windowList.ItemAt(index));
1458 	}
1459 
1460 	return window;
1461 }
1462 
1463 
1464 status_t
1465 BApplication::_GetWindowList(BList *list, bool includeMenus) const
1466 {
1467 	ASSERT(list);
1468 
1469 	// Windows are BLoopers, so we can just check each BLooper to see if it's
1470 	// a BWindow (or BMenuWindow)
1471 	BObjectLocker<BLooperList> listLock(gLooperList);
1472 	if (!listLock.IsLocked())
1473 		return B_ERROR;
1474 
1475 	BLooper *looper = NULL;
1476 	for (int32 i = 0; i < gLooperList.CountLoopers(); i++) {
1477 		looper = gLooperList.LooperAt(i);
1478 		if (dynamic_cast<BWindow *>(looper)) {
1479 			if (includeMenus || dynamic_cast<BMenuWindow *>(looper) == NULL)
1480 				list->AddItem(looper);
1481 		}
1482 	}
1483 
1484 	return B_OK;
1485 }
1486 
1487 
1488 //	#pragma mark -
1489 
1490 
1491 /*!
1492 	\brief Checks whether the supplied string is a valid application signature.
1493 
1494 	An error message is printed, if the string is no valid app signature.
1495 
1496 	\param signature The string to be checked.
1497 	\return
1498 	- \c B_OK: \a signature is a valid app signature.
1499 	- \c B_BAD_VALUE: \a signature is \c NULL or no valid app signature.
1500 */
1501 static status_t
1502 check_app_signature(const char *signature)
1503 {
1504 	bool isValid = false;
1505 	BMimeType type(signature);
1506 	if (type.IsValid() && !type.IsSupertypeOnly()
1507 		&& BMimeType("application").Contains(&type)) {
1508 		isValid = true;
1509 	}
1510 	if (!isValid) {
1511 		printf("bad signature (%s), must begin with \"application/\" and "
1512 			   "can't conflict with existing registered mime types inside "
1513 			   "the \"application\" media type.\n", signature);
1514 	}
1515 	return (isValid ? B_OK : B_BAD_VALUE);
1516 }
1517 
1518 
1519 /*!
1520 	\brief Returns the looper name for a given signature.
1521 
1522 	Normally this is "AppLooperPort", but in case of the registrar a
1523 	special name.
1524 
1525 	\return The looper name.
1526 */
1527 static const char *
1528 looper_name_for(const char *signature)
1529 {
1530 	if (signature && !strcasecmp(signature, kRegistrarSignature))
1531 		return BPrivate::get_roster_port_name();
1532 	return "AppLooperPort";
1533 }
1534 
1535 
1536 /*!
1537 	\brief Fills the passed BMessage with B_ARGV_RECEIVED infos.
1538 */
1539 static void
1540 fill_argv_message(BMessage &message)
1541 {
1542    	message.what = B_ARGV_RECEIVED;
1543 
1544 	int32 argc = __libc_argc;
1545 	const char * const *argv = __libc_argv;
1546 
1547 	// add argc
1548 	message.AddInt32("argc", argc);
1549 
1550 	// add argv
1551 	for (int32 i = 0; i < argc; i++)
1552 		message.AddString("argv", argv[i]);
1553 
1554 	// add current working directory
1555 	char cwd[B_PATH_NAME_LENGTH];
1556 	if (getcwd(cwd, B_PATH_NAME_LENGTH))
1557 		message.AddString("cwd", cwd);
1558 }
1559 
1560