Ignore:
Timestamp:
02/20/03 02:08:05 (20 years ago)
Author:
Nicholas Riley
Message:

appswitch 1.0

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/appswitch/appswitch/main.c

    r97 r99  
    2424const char *APP_NAME;
    2525
    26 #define VERSION "1.0b2"
     26#define VERSION "1.0"
    2727
    2828struct {
     
    3636    } matchType;
    3737    enum {
    38         APP_NONE, APP_SWITCH, APP_SHOW, APP_HIDE, APP_KILL, APP_LIST, APP_PRINT_PID
     38        APP_NONE, APP_SWITCH, APP_SHOW, APP_HIDE, APP_QUIT, APP_KILL, APP_KILL_HARD, APP_LIST, APP_PRINT_PID
    3939    } appAction;
     40    Boolean longList;
    4041    enum {
    4142        ACTION_NONE, ACTION_SHOW_ALL, ACTION_HIDE_OTHERS
     
    4647} OPTS =
    4748{
    48     kLSUnknownCreator, NULL, NULL, -1, NULL, MATCH_UNKNOWN, APP_NONE, ACTION_NONE, FINAL_NONE
     49    kLSUnknownCreator, NULL, NULL, -1, NULL, MATCH_UNKNOWN, APP_NONE, ACTION_NONE, FINAL_NONE, false
    4950};
    5051
     
    7071            "  -h            hide application\n"
    7172            "  -H            hide other applications\n"
    72             "  -k            kill application\n"
     73            "  -q            quit application\n"
     74            "  -k            kill application (SIGINT)\n"
     75            "  -K            kill application hard (SIGKILL)\n"
    7376            "  -l            list applications\n"
     77            "  -L            list applications including full paths and bundle identifiers\n"
    7478            "  -P            print application process ID\n"
    7579            "  -F            bring current application's windows to front\n"
     
    132136    if (argc == 1) usage();
    133137
    134     const char *opts = "c:i:p:a:sShHklPF";
     138    const char *opts = "c:i:p:a:sShHqkKlLPF";
    135139
    136140    while ( (ch = getopt(argc, argv, opts)) != -1) {
     
    159163                break;
    160164            case 's':
    161                 if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     165                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
    162166                OPTS.appAction = APP_SHOW;
    163167                break;
    164168            case 'h':
    165                 if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     169                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
    166170                OPTS.appAction = APP_HIDE;
    167171                break;
     172            case 'q':
     173                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
     174                OPTS.appAction = APP_QUIT;
     175                break;
    168176            case 'k':
    169                 if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     177                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
    170178                OPTS.appAction = APP_KILL;
    171179                break;
     180            case 'K':
     181                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
     182                OPTS.appAction = APP_KILL_HARD;
     183                break;
    172184            case 'l':
    173                 if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     185                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
    174186                OPTS.appAction = APP_LIST;
    175187                break;
     188            case 'L':
     189                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -L, -P options");
     190                OPTS.appAction = APP_LIST;
     191                OPTS.longList = true;
     192                break;
    176193            case 'P':
    177                 if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -k, -l, -P options");
     194                if (OPTS.appAction != APP_NONE) errexit("choose only one of -s, -h, -q, -k, -K, -l, -P options");
    178195                OPTS.appAction = APP_PRINT_PID;
    179196                break;
     
    227244}
    228245
     246Boolean bundleIdentifierForApplication(CFStringRef *bundleID, char *path) {
     247    CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), false);
     248    if (url == NULL) return false;
     249    CFBundleRef bundle = CFBundleCreate(NULL, url);
     250    if (bundle != NULL) {
     251        *bundleID = CFBundleGetIdentifier(bundle);
     252#if DEBUG
     253        CFShow(*bundleID);
     254#endif
     255    }
     256    CFRelease(url);
     257    return true;
     258}
     259
     260OSStatus quitApplication(CPSProcessSerNum *psn) {
     261    AppleEvent event;
     262    AEAddressDesc appDesc;
     263    OSStatus err;
     264
     265    AEInitializeDesc(&appDesc);
     266    err = AECreateDesc(typeProcessSerialNumber, psn, sizeof(*psn), &appDesc);
     267    if (err != noErr) return err;
     268
     269    // XXX AECreateAppleEvent is very slow in Mac OS X 10.2.4 and earlier.
     270    // XXX This is Apple's bug: <http://lists.apple.com/archives/applescript-implementors/2003/Feb/19/aecreateappleeventfromco.txt>
     271    err = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &appDesc, kAutoGenerateReturnID, kAnyTransactionID, &event);
     272    if (err != noErr) return err;
     273
     274    AppleEvent nullReply = {typeNull, nil};
     275    err = AESendMessage(&event, &nullReply, kAENoReply, kNoTimeOut);
     276    (void)AEDisposeDesc(&event);
     277    if (err != noErr) return err;
     278
     279    (void)AEDisposeDesc(&nullReply); // according to docs, don't call unless AESend returned successfully
     280
     281    return noErr;
     282}
     283
    229284CPSProcessSerNum matchApplication(CPSProcessInfoRec *info) {
    230285    long pathMaxLength = pathconf("/", _PC_PATH_MAX);
     
    249304        char *banner = "       PSN   PID TYPE CREA NAME                ";
    250305                     // 12345678.0 12345 1234 1234 12345678901234567890
    251         printf("%s PATH\n", banner);
    252306        if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) != -1 ||
    253307             ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) != -1 ||
    254308             ioctl(STDIN_FILENO,  TIOCGWINSZ, (char *)&ws) != -1) ||
    255309            ws.ws_col != 0) termwidth = ws.ws_col;
     310        char *formatButPath = "%8ld.%ld %5ld %c%c%c%c %c%c%c%c %-20.20s";
    256311        int pathlen = termwidth - strlen(banner) - 1;
    257         asprintf(&format, "%%8ld.%%ld %%5ld %%c%%c%%c%%c %%c%%c%%c%%c %%-20.20s %%-%d.%ds\n", pathlen, pathlen);
     312        // XXX don't ever free 'format', should fix if we get called repeatedly
     313        if (OPTS.longList) {
     314            printf("%s PATH (bundle identifier)\n", banner);
     315            asprintf(&format, "%s %%s", formatButPath);
     316        } else if (pathlen >= 4) {
     317            printf("%s PATH\n", banner);
     318            asprintf(&format, "%s %%-%d.%ds", formatButPath, pathlen, pathlen);
     319        } else {
     320            format = formatButPath;
     321        }
    258322    }
    259323   
     
    279343            case MATCH_BUNDLE_ID:
    280344               {
    281                    CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), false);
    282                    if (url == NULL) errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %ld)", name, psn.hi, psn.lo, info->UnixPID);
    283                    CFBundleRef bundle = CFBundleCreate(NULL, url);
    284                    if (bundle != NULL) {
    285                        CFStringRef bundleID = CFBundleGetIdentifier(bundle);
    286 #if DEBUG
    287                        CFShow(bundleID);
    288 #endif
    289                        if (bundleID != NULL && CFStringCompare(OPTS.bundleID, bundleID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
    290                            break;
    291                    }
    292                    CFRelease(url);
     345                   CFStringRef bundleID;
     346                   if (!bundleIdentifierForApplication(&bundleID, path))
     347                       errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %ld)", name, psn.hi, psn.lo, info->UnixPID);
     348                   if (bundleID != NULL && CFStringCompare(OPTS.bundleID, bundleID, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
     349                       break;
    293350                   continue;
    294351               }
     
    298355        if (OPTS.appAction == APP_LIST) {
    299356            char *type = (char *)&(info->ExecFileType), *crea = (char *)&(info->ExecFileCreator);
    300 #define CXX(c) ( (c) < ' ' ? '?' : (c) )
     357#define CXX(c) ( (c) < ' ' ? ' ' : (c) )
    301358#define OSTYPE_CHAR_ARGS(t) CXX(t[0]), CXX(t[1]), CXX(t[2]), CXX(t[3])
    302359            printf(format, psn.hi, psn.lo, info->UnixPID,
    303360                   OSTYPE_CHAR_ARGS(type), OSTYPE_CHAR_ARGS(crea),
    304361                   name, path);
     362            if (OPTS.longList) {
     363                CFStringRef bundleID = NULL;
     364                if (!bundleIdentifierForApplication(&bundleID, path))
     365                    errexit("can't get bundle location for process '%s' (PSN %ld.%ld, pid %ld)", name, psn.hi, psn.lo, info->UnixPID);
     366                if (bundleID != NULL) {
     367                    char *bundleIDStr = (char *)CFStringGetCStringPtr(bundleID, CFStringGetSystemEncoding());
     368                    if (bundleIDStr == NULL) {
     369                        CFIndex bundleIDLength = CFStringGetLength(bundleID) + 1;
     370                        bundleIDStr = (char *)malloc(bundleIDLength * sizeof(char));
     371                        if (!CFStringGetCString(bundleID, bundleIDStr, bundleIDLength, CFStringGetSystemEncoding())) {
     372                            CFShow(bundleIDStr);
     373                            errexit("internal error: string encoding conversion failed for bundle identifier");
     374                        }
     375                        printf(" (%s)", bundleIDStr);
     376                        free(bundleIDStr);
     377                    } else {
     378                        printf(" (%s)", bundleIDStr);
     379                    }
     380                    CFRelease(bundleID);
     381                }
     382            }
     383            putchar('\n');
    305384            continue;
    306385        }
     
    334413        case APP_SHOW: err = CPSPostShowReq(&psn); verb = "show"; break;
    335414        case APP_HIDE: err = CPSPostHideReq(&psn); verb = "hide"; break;
     415        case APP_QUIT: err = quitApplication(&psn); verb = "quit"; break;
    336416        case APP_KILL: err = CPSPostKillRequest(&psn, kNilOptions); verb = "kill"; break;
     417        case APP_KILL_HARD: err = CPSPostKillRequest(&psn, bfCPSKillHard); verb = "kill"; break;
    337418        case APP_PRINT_PID:
    338419            if (info.UnixPID <= 0) errexit("can't get process ID");
Note: See TracChangeset for help on using the changeset viewer.