source: releases/appswitch/1.0d1/main.c@ 568

Last change on this file since 568 was 85, checked in by Nicholas Riley, 22 years ago

Initial import.

File size: 7.9 KB
Line 
1/*
2 appswitch - a command-line application switcher
3 Nicholas Riley <appswitch@sabi.net>
4
5 Copyright (c) 2003, Nicholas Riley
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
9
10 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
12 * Neither the name of this software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
16*/
17
18#define DEBUG 0
19
20#include <unistd.h>
21#include "CPS.h"
22
23const char *APP_NAME;
24
25#define VERSION "1.0d1"
26
27struct {
28 OSType creator;
29 CFStringRef bundleID;
30 char *name;
31 pid_t pid;
32 char *path;
33 enum {
34 MATCH_UNKNOWN, MATCH_CREATOR, MATCH_BUNDLE_ID, MATCH_NAME, MATCH_PID, MATCH_PATH
35 } matchType;
36} OPTS =
37{
38 kLSUnknownCreator, NULL, NULL, -1, NULL, MATCH_UNKNOWN
39};
40
41typedef struct {
42 OSStatus status;
43 const char *desc;
44} errRec, errList[];
45
46static errList ERRS = {
47 // Process Manager errors
48 { appIsDaemon, "application is background-only\n", },
49 { procNotFound, "unable to connect to system service.\nAre you logged in?" },
50 // CoreGraphics errors
51 { kCGErrorIllegalArgument, "window server error.\nAre you logged in?" },
52 { fnfErr, "file not found" },
53 { 0, NULL }
54};
55
56void usage() {
57 fprintf(stderr, "usage: %s [-c creator] [-i bundleID] [-a name] [-p pid] [path]\n"
58 " -c creator match application by four-character creator code ('ToyS')\n"
59 " -i bundle ID match application by bundle identifier (com.apple.scripteditor)\n"
60 " -p pid match application by process identifier [slower]\n"
61 " -a name match application by name\n", APP_NAME);
62 fprintf(stderr, "appswitch "VERSION" (c) 2003 Nicholas Riley <http://web.sabi.net/nriley/software/>.\n"
63 "Please send bugs, suggestions, etc. to <appswitch@sabi.net>.\n");
64
65 exit(1);
66}
67
68char *osstatusstr(OSStatus err) {
69 errRec *rec;
70 const char *errDesc = "unknown error";
71 char * const failedStr = "(unable to retrieve error message)";
72 static char *str = NULL;
73 size_t len;
74 if (str != NULL && str != failedStr) free(str);
75 for (rec = &(ERRS[0]) ; rec->status != 0 ; rec++)
76 if (rec->status == err) {
77 errDesc = rec->desc;
78 break;
79 }
80 len = strlen(errDesc) + 10 * sizeof(char);
81 str = (char *)malloc(len);
82 if (str != NULL)
83 snprintf(str, len, "%s (%ld)", errDesc, err);
84 else
85 str = failedStr;
86 return str;
87}
88
89void osstatusexit(OSStatus err, const char *fmt, ...) {
90 va_list ap;
91 const char *errDesc = osstatusstr(err);
92 va_start(ap, fmt);
93 fprintf(stderr, "%s: ", APP_NAME);
94 vfprintf(stderr, fmt, ap);
95 fprintf(stderr, ": %s\n", errDesc);
96 exit(1);
97}
98
99void errexit(const char *fmt, ...) {
100 va_list ap;
101 va_start(ap, fmt);
102 fprintf(stderr, "%s: ", APP_NAME);
103 vfprintf(stderr, fmt, ap);
104 fprintf(stderr, "\n");
105 exit(1);
106}
107
108void getargs(int argc, char * const argv[]) {
109 extern char *optarg;
110 extern int optind;
111 int ch;
112
113 if (argc == 1) usage();
114
115 const char *opts = "c:i:p:a:";
116
117 ch = getopt(argc, argv, opts);
118
119 switch (ch) {
120 case 'p':
121 if (sscanf(optarg, "%d", &OPTS.pid) != 1 || OPTS.pid < 0)
122 errexit("invalid process identifier (argument of -p)");
123 OPTS.matchType = MATCH_PID;
124 break;
125 case 'c':
126 if (strlen(optarg) != 4) errexit("creator (argument of -c) must be four characters long");
127 OPTS.creator = *(OSTypePtr)optarg;
128 OPTS.matchType = MATCH_CREATOR;
129 break;
130 case 'i':
131 OPTS.bundleID = CFStringCreateWithCString(NULL, optarg, CFStringGetSystemEncoding());
132 OPTS.matchType = MATCH_BUNDLE_ID;
133 break;
134 case 'a':
135 OPTS.name = strdup(optarg);
136 OPTS.matchType = MATCH_NAME;
137 break;
138 case -1:
139 break;
140 default: usage();
141 }
142
143 if (getopt(argc, argv, opts) != -1)
144 errexit("choose only one of -c, -i, -u, -p, -a options");
145
146 argc -= optind;
147 argv += optind;
148
149 if (OPTS.matchType != MATCH_UNKNOWN && argc != 0) usage();
150
151 if (OPTS.matchType == MATCH_UNKNOWN) {
152 if (argc != 1) usage();
153 OPTS.path = argv[0];
154 OPTS.matchType = MATCH_PATH;
155 }
156}
157
158int main (int argc, char * const argv[]) {
159 OSStatus err;
160
161 APP_NAME = argv[0];
162 getargs(argc, argv);
163
164 long pathMaxLength = pathconf("/", _PC_PATH_MAX);
165 long nameMaxLength = pathconf("/", _PC_NAME_MAX);
166
167 char *path = (char *)malloc(pathMaxLength);
168 char *name = (char *)malloc(nameMaxLength);;
169
170 if (path == NULL || name == NULL) errexit("can't allocate memory for path or filename buffer");
171
172 // need to establish connection with window server
173 InitCursor();
174
175 CPSProcessSerNum psn = {
176 kNoProcess, kNoProcess
177 };
178 CPSProcessInfoRec info;
179 int len;
180 while ( (err = CPSGetNextProcess(&psn)) == noErr) {
181 err = CPSGetProcessInfo(&psn, &info, path, pathMaxLength, &len, name, nameMaxLength);
182 if (err != noErr) osstatusexit(err, "can't get information for process PSN %ld.%ld", psn.hi, psn.lo);
183
184#if DEBUG
185 fprintf(stderr, "%ld.%ld: %s : %s\n", psn.hi, psn.lo, name, path);
186#endif
187
188 switch (OPTS.matchType) {
189 case MATCH_UNKNOWN:
190 break;
191 case MATCH_CREATOR: if (OPTS.creator != info.ExecFileCreator) continue;
192 break;
193 case MATCH_NAME: if (strcmp(name, OPTS.name) != 0) continue;
194 break;
195 case MATCH_PID: if (OPTS.pid != info.UnixPID) continue;
196 break;
197 case MATCH_PATH: if (strcmp(path, OPTS.path) != 0) continue;
198 break;
199 case MATCH_BUNDLE_ID:
200 {
201 CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), false);
202 if (url == NULL) errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %d)", name, psn.hi, psn.lo, info.UnixPID);
203 CFBundleRef bundle = CFBundleCreate(NULL, url);
204 if (bundle != NULL) {
205 CFStringRef bundleID = CFBundleGetIdentifier(bundle);
206#if DEBUG
207 CFShow(bundleID);
208#endif
209 if (bundleID != NULL && CFStringCompare(OPTS.bundleID, bundleID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
210 break;
211 }
212 CFRelease(url);
213 continue;
214 }
215 default:
216 errexit("unknown match type");
217 }
218 err = CPSSetFrontProcess(&psn);
219 if (err != noErr) osstatusexit(err, "can't set front process");
220 exit(0);
221 }
222 if (err != procNotFound) osstatusexit(err, "can't get next process");
223
224 errexit("can't find matching process");
225 return 0;
226}
Note: See TracBrowser for help on using the repository browser.