(linenum→info "unix/slp.c:2238")

emacs/22.1/src/mac.c

    1: /* Unix emulation routines for GNU Emacs on the Mac OS.
    2:    Copyright (C) 2000, 2001, 2002, 2003, 2004,
    3:                  2005, 2006, 2007 Free Software Foundation, Inc.
    4: 
    5: This file is part of GNU Emacs.
    6: 
    7: GNU Emacs is free software; you can redistribute it and/or modify
    8: it under the terms of the GNU General Public License as published by
    9: the Free Software Foundation; either version 2, or (at your option)
   10: any later version.
   11: 
   12: GNU Emacs is distributed in the hope that it will be useful,
   13: but WITHOUT ANY WARRANTY; without even the implied warranty of
   14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15: GNU General Public License for more details.
   16: 
   17: You should have received a copy of the GNU General Public License
   18: along with GNU Emacs; see the file COPYING.  If not, write to
   19: the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   20: Boston, MA 02110-1301, USA.  */
   21: 
   22: /* Contributed by Andrew Choi (akochoi@mac.com).  */
   23: 
   24: #include <config.h>
   25: 
   26: #include <stdio.h>
   27: #include <errno.h>
   28: 
   29: #include "lisp.h"
   30: #include "process.h"
   31: #ifdef MAC_OSX
   32: #undef select
   33: #endif
   34: #include "systime.h"
   35: #include "sysselect.h"
   36: #include "blockinput.h"
   37: 
   38: #include "macterm.h"
   39: 
   40: #include "charset.h"
   41: #include "coding.h"
   42: #if !TARGET_API_MAC_CARBON
   43: #include <Files.h>
   44: #include <MacTypes.h>
   45: #include <TextUtils.h>
   46: #include <Folders.h>
   47: #include <Resources.h>
   48: #include <Aliases.h>
   49: #include <Timer.h>
   50: #include <OSA.h>
   51: #include <AppleScript.h>
   52: #include <Events.h>
   53: #include <Processes.h>
   54: #include <EPPC.h>
   55: #include <MacLocales.h>
   56: #include <Endian.h>
   57: #endif  /* not TARGET_API_MAC_CARBON */
   58: 
   59: #include <utime.h>
   60: #include <dirent.h>
   61: #include <sys/types.h>
   62: #include <sys/stat.h>
   63: #include <pwd.h>
   64: #include <grp.h>
   65: #include <sys/param.h>
   66: #include <fcntl.h>
   67: #if __MWERKS__
   68: #include <unistd.h>
   69: #endif
   70: 
   71: /* The system script code. */
   72: static int mac_system_script_code;
   73: 
   74: /* The system locale identifier string.  */
   75: static Lisp_Object Vmac_system_locale;
   76: 
   77: /* An instance of the AppleScript component.  */
   78: static ComponentInstance as_scripting_component;
   79: /* The single script context used for all script executions.  */
   80: static OSAID as_script_context;
   81: 
   82: #if TARGET_API_MAC_CARBON
   83: static int wakeup_from_rne_enabled_p = 0;
   84: #define ENABLE_WAKEUP_FROM_RNE (wakeup_from_rne_enabled_p = 1)
   85: #define DISABLE_WAKEUP_FROM_RNE (wakeup_from_rne_enabled_p = 0)
   86: #else
   87: #define ENABLE_WAKEUP_FROM_RNE 0
   88: #define DISABLE_WAKEUP_FROM_RNE 0
   89: #endif
   90: 
   91: #ifndef MAC_OSX
   92: static OSErr posix_pathname_to_fsspec P_ ((const char *, FSSpec *));
   93: static OSErr fsspec_to_posix_pathname P_ ((const FSSpec *, char *, int));
   94: #endif
   95: 
   96: /* When converting from Mac to Unix pathnames, /'s in folder names are
   97:    converted to :'s.  This function, used in copying folder names,
   98:    performs a strncat and converts all character a to b in the copy of
   99:    the string s2 appended to the end of s1.  */
  100: 
  101: void
  102: string_cat_and_replace (char *s1, const char *s2, int n, char a, char b)
  103: {
  104:   int l1 = strlen (s1);
  105:   int l2 = strlen (s2);
  106:   char *p = s1 + l1;
  107:   int i;
  108: 
  109:   strncat (s1, s2, n);
  110:   for (i = 0; i < l2; i++)
  111:     {
  112:       if (*p == a)
  113:         *p = b;
  114:       p++;
  115:     }
  116: }
  117: 
  118: 
  119: /* Convert a Mac pathname to Posix form.  A Mac full pathname is one
  120:    that does not begin with a ':' and contains at least one ':'. A Mac
  121:    full pathname causes a '/' to be prepended to the Posix pathname.
  122:    The algorithm for the rest of the pathname is as follows:
  123:      For each segment between two ':',
  124:        if it is non-null, copy as is and then add a '/' at the end,
  125:        otherwise, insert a "../" into the Posix pathname.
  126:    Returns 1 if successful; 0 if fails.  */
  127: 
  128: int
  129: mac_to_posix_pathname (const char *mfn, char *ufn, int ufnbuflen)
  130: {
  131:   const char *p, *q, *pe;
  132: 
  133:   strcpy (ufn, "");
  134: 
  135:   if (*mfn == '\0')
  136:     return 1;
  137: 
  138:   p = strchr (mfn, ':');
  139:   if (p != 0 && p != mfn)  /* full pathname */
  140:     strcat (ufn, "/");
  141: 
  142:   p = mfn;
  143:   if (*p == ':')
  144:     p++;
  145: 
  146:   pe = mfn + strlen (mfn);
  147:   while (p < pe)
  148:     {
  149:       q = strchr (p, ':');
  150:       if (q)
  151:         {
  152:           if (q == p)
  153:             {  /* two consecutive ':' */
  154:               if (strlen (ufn) + 3 >= ufnbuflen)
  155:                 return 0;
  156:               strcat (ufn, "../");
  157:             }
  158:           else
  159:             {
  160:               if (strlen (ufn) + (q - p) + 1 >= ufnbuflen)
  161:                 return 0;
  162:               string_cat_and_replace (ufn, p, q - p, '/', ':');
  163:               strcat (ufn, "/");
  164:             }
  165:           p = q + 1;
  166:         }
  167:       else
  168:         {
  169:           if (strlen (ufn) + (pe - p) >= ufnbuflen)
  170:             return 0;
  171:           string_cat_and_replace (ufn, p, pe - p, '/', ':');
  172:             /* no separator for last one */
  173:           p = pe;
  174:         }
  175:     }
  176: 
  177:   return 1;
  178: }
  179: 
  180: 
  181: extern char *get_temp_dir_name ();
  182: 
  183: 
  184: /* Convert a Posix pathname to Mac form.  Approximately reverse of the
  185:    above in algorithm.  */
  186: 
  187: int
  188: posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
  189: {
  190:   const char *p, *q, *pe;
  191:   char expanded_pathname[MAXPATHLEN+1];
  192: 
  193:   strcpy (mfn, "");
  194: 
  195:   if (*ufn == '\0')
  196:     return 1;
  197: 
  198:   p = ufn;
  199: 
  200:   /* Check for and handle volume names.  Last comparison: strangely
  201:      somewhere "/.emacs" is passed.  A temporary fix for now.  */
  202:   if (*p == '/' && strchr (p+1, '/') == NULL && strcmp (p, "/.emacs") != 0)
  203:     {
  204:       if (strlen (p) + 1 > mfnbuflen)
  205:         return 0;
  206:       strcpy (mfn, p+1);
  207:       strcat (mfn, ":");
  208:       return 1;
  209:     }
  210: 
  211:   /* expand to emacs dir found by init_emacs_passwd_dir */
  212:   if (strncmp (p, "~emacs/", 7) == 0)
  213:     {
  214:       struct passwd *pw = getpwnam ("emacs");
  215:       p += 7;
  216:       if (strlen (pw->pw_dir) + strlen (p) > MAXPATHLEN)
  217:         return 0;
  218:       strcpy (expanded_pathname, pw->pw_dir);
  219:       strcat (expanded_pathname, p);
  220:       p = expanded_pathname;
  221:         /* now p points to the pathname with emacs dir prefix */
  222:     }
  223:   else if (strncmp (p, "/tmp/", 5) == 0)
  224:     {
  225:       char *t = get_temp_dir_name ();
  226:       p += 5;
  227:       if (strlen (t) + strlen (p) > MAXPATHLEN)
  228:         return 0;
  229:       strcpy (expanded_pathname, t);
  230:       strcat (expanded_pathname, p);
  231:       p = expanded_pathname;
  232:         /* now p points to the pathname with emacs dir prefix */
  233:     }
  234:   else if (*p != '/')  /* relative pathname */
  235:     strcat (mfn, ":");
  236: 
  237:   if (*p == '/')
  238:     p++;
  239: 
  240:   pe = p + strlen (p);
  241:   while (p < pe)
  242:     {
  243:       q = strchr (p, '/');
  244:       if (q)
  245:         {
  246:           if (q - p == 2 && *p == '.' && *(p+1) == '.')
  247:             {
  248:               if (strlen (mfn) + 1 >= mfnbuflen)
  249:                 return 0;
  250:               strcat (mfn, ":");
  251:             }
  252:           else
  253:             {
  254:               if (strlen (mfn) + (q - p) + 1 >= mfnbuflen)
  255:                 return 0;
  256:               string_cat_and_replace (mfn, p, q - p, ':', '/');
  257:               strcat (mfn, ":");
  258:             }
  259:           p = q + 1;
  260:         }
  261:       else
  262:         {
  263:           if (strlen (mfn) + (pe - p) >= mfnbuflen)
  264:             return 0;
  265:           string_cat_and_replace (mfn, p, pe - p, ':', '/');
  266:           p = pe;
  267:         }
  268:     }
  269: 
  270:   return 1;
  271: }
  272: 
  273: ^L
  274: /***********************************************************************
  275:                   Conversions on Apple event objects
  276:  ***********************************************************************/
  277: 
  278: static Lisp_Object Qundecoded_file_name;
  279: 
  280: static struct {
  281:   AEKeyword keyword;
  282:   char *name;
  283:   Lisp_Object symbol;
  284: } ae_attr_table [] =
  285:   {{keyTransactionIDAttr,       "transaction-id"},
  286:    {keyReturnIDAttr,            "return-id"},
  287:    {keyEventClassAttr,          "event-class"},
  288:    {keyEventIDAttr,             "event-id"},
  289:    {keyAddressAttr,             "address"},
  290:    {keyOptionalKeywordAttr,     "optional-keyword"},
  291:    {keyTimeoutAttr,             "timeout"},
  292:    {keyInteractLevelAttr,       "interact-level"},
  293:    {keyEventSourceAttr,         "event-source"},
  294:    /* {keyMissedKeywordAttr,    "missed-keyword"}, */
  295:    {keyOriginalAddressAttr,     "original-address"},
  296:    {keyReplyRequestedAttr,      "reply-requested"},
  297:    {KEY_EMACS_SUSPENSION_ID_ATTR, "emacs-suspension-id"}
  298:   };
  299: 
  300: static Lisp_Object
  301: mac_aelist_to_lisp (desc_list)
  302:      const AEDescList *desc_list;
  303: {
  304:   OSErr err;
  305:   long count;
  306:   Lisp_Object result, elem;
  307:   DescType desc_type;
  308:   Size size;
  309:   AEKeyword keyword;
  310:   AEDesc desc;
  311:   int attribute_p = 0;
  312: 
  313:   err = AECountItems (desc_list, &count);
  314:   if (err != noErr)
  315:     return Qnil;
  316:   result = Qnil;
  317: 
  318:  again:
  319:   while (count > 0)
  320:     {
  321:       if (attribute_p)
  322:         {
  323:           keyword = ae_attr_table[count - 1].keyword;
  324:           err = AESizeOfAttribute (desc_list, keyword, &desc_type, &size);
  325:         }
  326:       else
  327:         err = AESizeOfNthItem (desc_list, count, &desc_type, &size);
  328: 
  329:       if (err == noErr)
  330:         switch (desc_type)
  331:           {
  332:           case typeAEList:
  333:           case typeAERecord:
  334:           case typeAppleEvent:
  335:             if (attribute_p)
  336:               err = AEGetAttributeDesc (desc_list, keyword, typeWildCard,
  337:                                         &desc);
  338:             else
  339:               err = AEGetNthDesc (desc_list, count, typeWildCard,
  340:                                   &keyword, &desc);
  341:             if (err != noErr)
  342:               break;
  343:             elem = mac_aelist_to_lisp (&desc);
  344:             AEDisposeDesc (&desc);
  345:             break;
  346: 
  347:           default:
  348:             if (desc_type == typeNull)
  349:               elem = Qnil;
  350:             else
  351:               {
  352:                 elem = make_uninit_string (size);
  353:                 if (attribute_p)
  354:                   err = AEGetAttributePtr (desc_list, keyword, typeWildCard,
  355:                                            &desc_type, SDATA (elem),
  356:                                            size, &size);
  357:                 else
  358:                   err = AEGetNthPtr (desc_list, count, typeWildCard, &keyword,
  359:                                      &desc_type, SDATA (elem), size, &size);
  360:               }
  361:             if (err != noErr)
  362:               break;
  363:             desc_type = EndianU32_NtoB (desc_type);
  364:             elem = Fcons (make_unibyte_string ((char *) &desc_type, 4), elem);
  365:             break;
  366:         }
  367: 
  368:       if (err == noErr || desc_list->descriptorType == typeAEList)
  369:         {
  370:           if (err != noErr)
  371:             elem = Qnil;       /* Don't skip elements in AEList.  */
  372:           else if (desc_list->descriptorType != typeAEList)
  373:             {
  374:               if (attribute_p)
  375:                 elem = Fcons (ae_attr_table[count-1].symbol, elem);
  376:               else
  377:                 {
  378:                   keyword = EndianU32_NtoB (keyword);
  379:                   elem = Fcons (make_unibyte_string ((char *) &keyword, 4),
  380:                                 elem);
  381:                 }
  382:             }
  383: 
  384:           result = Fcons (elem, result);
  385:         }
  386: 
  387:       count--;
  388:     }
  389: 
  390:   if (desc_list->descriptorType == typeAppleEvent && !attribute_p)
  391:     {
  392:       attribute_p = 1;
  393:       count = sizeof (ae_attr_table) / sizeof (ae_attr_table[0]);
  394:       goto again;
  395:     }
  396: 
  397:   desc_type = EndianU32_NtoB (desc_list->descriptorType);
  398:   return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
  399: }
  400: 
  401: Lisp_Object
  402: mac_aedesc_to_lisp (desc)
  403:      const AEDesc *desc;
  404: {
  405:   OSErr err = noErr;
  406:   DescType desc_type = desc->descriptorType;
  407:   Lisp_Object result;
  408: 
  409:   switch (desc_type)
  410:     {
  411:     case typeNull:
  412:       result = Qnil;
  413:       break;
  414: 
  415:     case typeAEList:
  416:     case typeAERecord:
  417:     case typeAppleEvent:
  418:       return mac_aelist_to_lisp (desc);
  419: #if 0
  420:       /* The following one is much simpler, but creates and disposes
  421:          of Apple event descriptors many times.  */
  422:       {
  423:         long count;
  424:         Lisp_Object elem;
  425:         AEKeyword keyword;
  426:         AEDesc desc1;
  427: 
  428:         err = AECountItems (desc, &count);
  429:         if (err != noErr)
  430:           break;
  431:         result = Qnil;
  432:         while (count > 0)
  433:           {
  434:             err = AEGetNthDesc (desc, count, typeWildCard, &keyword, &desc1);
  435:             if (err != noErr)
  436:               break;
  437:             elem = mac_aedesc_to_lisp (&desc1);
  438:             AEDisposeDesc (&desc1);
  439:             if (desc_type != typeAEList)
  440:               {
  441:                 keyword = EndianU32_NtoB (keyword);
  442:                 elem = Fcons (make_unibyte_string ((char *) &keyword, 4), elem);
  443:               }
  444:             result = Fcons (elem, result);
  445:             count--;
  446:           }
  447:       }
  448: #endif
  449:       break;
  450: 
  451:     default:
  452: #if TARGET_API_MAC_CARBON
  453:       result = make_uninit_string (AEGetDescDataSize (desc));
  454:       err = AEGetDescData (desc, SDATA (result), SBYTES (result));
  455: #else
  456:       result = make_uninit_string (GetHandleSize (desc->dataHandle));
  457:       memcpy (SDATA (result), *(desc->dataHandle), SBYTES (result));
  458: #endif
  459:       break;
  460:     }
  461: 
  462:   if (err != noErr)
  463:     return Qnil;
  464: 
  465:   desc_type = EndianU32_NtoB (desc_type);
  466:   return Fcons (make_unibyte_string ((char *) &desc_type, 4), result);
  467: }
  468: 
  469: OSErr
  470: mac_ae_put_lisp (desc, keyword_or_index, obj)
  471:      AEDescList *desc;
  472:      UInt32 keyword_or_index;
  473:      Lisp_Object obj;
  474: {
  475:   OSErr err;
  476: 
  477:   if (!(desc->descriptorType == typeAppleEvent
  478:         || desc->descriptorType == typeAERecord
  479:         || desc->descriptorType == typeAEList))
  480:     return errAEWrongDataType;
  481: 
  482:   if (CONSP (obj) && STRINGP (XCAR (obj)) && SBYTES (XCAR (obj)) == 4)
  483:     {
  484:       DescType desc_type1 = EndianU32_BtoN (*((UInt32 *) SDATA (XCAR (obj))));
  485:       Lisp_Object data = XCDR (obj), rest;
  486:       AEDesc desc1;
  487: 
  488:       switch (desc_type1)
  489:         {
  490:         case typeNull:
  491:         case typeAppleEvent:
  492:           break;
  493: 
  494:         case typeAEList:
  495:         case typeAERecord:
  496:           err = AECreateList (NULL, 0, desc_type1 == typeAERecord, &desc1);
  497:           if (err == noErr)
  498:             {
  499:               for (rest = data; CONSP (rest); rest = XCDR (rest))
  500:                 {
  501:                   UInt32 keyword_or_index1 = 0;
  502:                   Lisp_Object elem = XCAR (rest);
  503: 
  504:                   if (desc_type1 == typeAERecord)
  505:                     {
  506:                       if (CONSP (elem) && STRINGP (XCAR (elem))
  507:                           && SBYTES (XCAR (elem)) == 4)
  508:                         {
  509:                           keyword_or_index1 =
  510:                             EndianU32_BtoN (*((UInt32 *)
  511:                                               SDATA (XCAR (elem))));
  512:                           elem = XCDR (elem);
  513:                         }
  514:                       else
  515:                         continue;
  516:                     }
  517: 
  518:                   err = mac_ae_put_lisp (&desc1, keyword_or_index1, elem);
  519:                   if (err != noErr)
  520:                     break;
  521:                 }
  522: 
  523:               if (err == noErr)
  524:                 {
  525:                   if (desc->descriptorType == typeAEList)
  526:                     err = AEPutDesc (desc, keyword_or_index, &desc1);
  527:                   else
  528:                     err = AEPutParamDesc (desc, keyword_or_index, &desc1);
  529:                 }
  530: 
  531:               AEDisposeDesc (&desc1);
  532:             }
  533:           return err;
  534: 
  535:         default:
  536:           if (!STRINGP (data))
  537:             break;
  538:           if (desc->descriptorType == typeAEList)
  539:             err = AEPutPtr (desc, keyword_or_index, desc_type1,
  540:                             SDATA (data), SBYTES (data));
  541:           else
  542:             err = AEPutParamPtr (desc, keyword_or_index, desc_type1,
  543:                                  SDATA (data), SBYTES (data));
  544:           return err;
  545:         }
  546:     }
  547: 
  548:   if (desc->descriptorType == typeAEList)
  549:     err = AEPutPtr (desc, keyword_or_index, typeNull, NULL, 0);
  550:   else
  551:     err = AEPutParamPtr (desc, keyword_or_index, typeNull, NULL, 0);
  552: 
  553:   return err;
  554: }
  555: 
  556: static pascal OSErr
  557: mac_coerce_file_name_ptr (type_code, data_ptr, data_size,
  558:                           to_type, handler_refcon, result)
  559:      DescType type_code;
  560:      const void *data_ptr;
  561:      Size data_size;
  562:      DescType to_type;
  563:      long handler_refcon;
  564:      AEDesc *result;
  565: {
  566:   OSErr err;
  567: 
  568:   if (type_code == typeNull)
  569:     err = errAECoercionFail;
  570:   else if (type_code == to_type || to_type == typeWildCard)
  571:     err = AECreateDesc (TYPE_FILE_NAME, data_ptr, data_size, result);
  572:   else if (type_code == TYPE_FILE_NAME)
  573:     /* Coercion from undecoded file name.  */
  574:     {
  575: #ifdef MAC_OSX
  576:       CFStringRef str;
  577:       CFURLRef url = NULL;
  578:       CFDataRef data = NULL;
  579: 
  580:       str = CFStringCreateWithBytes (NULL, data_ptr, data_size,
  581:                                      kCFStringEncodingUTF8, false);
  582:       if (str)
  583:         {
  584:           url = CFURLCreateWithFileSystemPath (NULL, str,
  585:                                                kCFURLPOSIXPathStyle, false);
  586:           CFRelease (str);
  587:         }
  588:       if (url)
  589:         {
  590:           data = CFURLCreateData (NULL, url, kCFStringEncodingUTF8, true);
  591:           CFRelease (url);
  592:         }
  593:       if (data)
  594:         {
  595:           err = AECoercePtr (typeFileURL, CFDataGetBytePtr (