* dbus/dbus-sysdeps-win.h, dbus/dbus-sysdeps-win.c, dbus/dbus-sysdeps-util-win.c...
[platform/upstream/dbus.git] / dbus / dbus-sysdeps-util-win.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-sysdeps-util.c Would be in dbus-sysdeps.c, but not used in libdbus
3  * 
4  * Copyright (C) 2002, 2003, 2004, 2005  Red Hat, Inc.
5  * Copyright (C) 2003 CodeFactory AB
6  *
7  * Licensed under the Academic Free License version 2.1
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #undef open
26
27 #define STRSAFE_NO_DEPRECATE
28
29 #include "dbus-sysdeps.h"
30 #include "dbus-internals.h"
31 #include "dbus-protocol.h"
32 #include "dbus-string.h"
33 #include "dbus-sysdeps.h"
34 #include "dbus-sysdeps-win.h"
35 #include "dbus-memory.h"
36
37 #include <io.h>
38 #include <sys/stat.h>
39 #include <aclapi.h>
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <fcntl.h>
44
45 /**
46  * Does the chdir, fork, setsid, etc. to become a daemon process.
47  *
48  * @param pidfile #NULL, or pidfile to create
49  * @param print_pid_fd file descriptor to print daemon's pid to, or -1 for none
50  * @param error return location for errors
51  * @returns #FALSE on failure
52  */
53 dbus_bool_t
54 _dbus_become_daemon (const DBusString *pidfile,
55                      DBusPipe         print_pid_fd,
56                      DBusError        *error)
57 {
58   return TRUE;
59 }
60
61 /**
62  * Creates a file containing the process ID.
63  *
64  * @param filename the filename to write to
65  * @param pid our process ID
66  * @param error return location for errors
67  * @returns #FALSE on failure
68  */
69 dbus_bool_t
70 _dbus_write_pid_file (const DBusString *filename,
71                       unsigned long     pid,
72                       DBusError        *error)
73 {
74   const char *cfilename;
75   DBusFile file;
76   FILE *f;
77
78   cfilename = _dbus_string_get_const_data (filename);
79
80   if (!_dbus_file_open(&file, cfilename, O_WRONLY|O_CREAT|O_EXCL|O_BINARY, 0644))
81     {
82       dbus_set_error (error, _dbus_error_from_errno (errno),
83                       "Failed to open \"%s\": %s", cfilename,
84                       _dbus_strerror (errno));
85       return FALSE;
86     }
87
88   if ((f = fdopen (file.FDATA, "w")) == NULL)
89     {
90       dbus_set_error (error, _dbus_error_from_errno (errno),
91                       "Failed to fdopen fd %d: %s", file.FDATA, _dbus_strerror (errno));
92       _dbus_file_close (&file, NULL);
93       return FALSE;
94     }
95
96   if (fprintf (f, "%lu\n", pid) < 0)
97     {
98       dbus_set_error (error, _dbus_error_from_errno (errno),
99                       "Failed to write to \"%s\": %s", cfilename,
100                       _dbus_strerror (errno));
101
102       fclose (f);
103       return FALSE;
104     }
105
106   if (fclose (f) == EOF)
107     {
108       dbus_set_error (error, _dbus_error_from_errno (errno),
109                       "Failed to close \"%s\": %s", cfilename,
110                       _dbus_strerror (errno));
111       return FALSE;
112     }
113
114   return TRUE;
115 }
116
117 /**
118  * Changes the user and group the bus is running as.
119  *
120  * @param uid the new user ID
121  * @param gid the new group ID
122  * @param error return location for errors
123  * @returns #FALSE on failure
124  */
125 dbus_bool_t
126 _dbus_change_identity  (dbus_uid_t     uid,
127                         dbus_gid_t     gid,
128                         DBusError     *error)
129 {
130   return TRUE;
131 }
132
133 /** Checks if user is at the console
134 *
135 * @param username user to check
136 * @param error return location for errors
137 * @returns #TRUE is the user is at the consolei and there are no errors
138 */
139 dbus_bool_t
140 _dbus_user_at_console(const char *username,
141                       DBusError  *error)
142 {
143 #ifdef DBUS_WINCE
144         return TRUE;
145 #else
146   dbus_bool_t retval = FALSE;
147   wchar_t *wusername;
148   DWORD sid_length;
149   PSID user_sid, console_user_sid;
150   HWINSTA winsta;
151
152   wusername = _dbus_win_utf8_to_utf16 (username, error);
153   if (!wusername)
154     return FALSE;
155
156   if (!_dbus_win_account_to_sid (wusername, &user_sid, error))
157     goto out0;
158
159   /* Now we have the SID for username. Get the SID of the
160    * user at the "console" (window station WinSta0)
161    */
162   if (!(winsta = OpenWindowStation ("WinSta0", FALSE, READ_CONTROL)))
163     {
164       _dbus_win_set_error_from_win_error (error, GetLastError ());
165       goto out2;
166     }
167
168   sid_length = 0;
169   GetUserObjectInformation (winsta, UOI_USER_SID,
170                             NULL, 0, &sid_length);
171   if (sid_length == 0)
172     {
173       /* Nobody is logged on */
174       goto out2;
175     }
176
177   if (sid_length < 0 || sid_length > 1000)
178     {
179       dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID length");
180       goto out3;
181     }
182
183   console_user_sid = dbus_malloc (sid_length);
184   if (!console_user_sid)
185     {
186       _DBUS_SET_OOM (error);
187       goto out3;
188     }
189
190   if (!GetUserObjectInformation (winsta, UOI_USER_SID,
191                                  console_user_sid, sid_length, &sid_length))
192     {
193       _dbus_win_set_error_from_win_error (error, GetLastError ());
194       goto out4;
195     }
196
197   if (!IsValidSid (console_user_sid))
198     {
199       dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid SID");
200       goto out4;
201     }
202
203   retval = EqualSid (user_sid, console_user_sid);
204
205 out4:
206   dbus_free (console_user_sid);
207 out3:
208   CloseWindowStation (winsta);
209 out2:
210   dbus_free (user_sid);
211 out0:
212   dbus_free (wusername);
213
214   return retval;
215 #endif //DBUS_WINCE
216 }
217
218 /**
219  * Removes a directory; Directory must be empty
220  * 
221  * @param filename directory filename
222  * @param error initialized error object
223  * @returns #TRUE on success
224  */
225 dbus_bool_t
226 _dbus_delete_directory (const DBusString *filename,
227                         DBusError        *error)
228 {
229   const char *filename_c;
230
231   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
232
233   filename_c = _dbus_string_get_const_data (filename);
234
235   if (rmdir (filename_c) != 0)
236     {
237       dbus_set_error (error, DBUS_ERROR_FAILED,
238                       "Failed to remove directory %s: %s\n",
239                       filename_c, _dbus_strerror (errno));
240       return FALSE;
241     }
242
243   return TRUE;
244 }
245
246 /** Installs a signal handler
247  *
248  * @param sig the signal to handle
249  * @param handler the handler
250  */
251 void
252 _dbus_set_signal_handler (int               sig,
253                           DBusSignalHandler handler)
254 {
255   _dbus_verbose ("_dbus_set_signal_handler() has to be implemented\n");
256 }
257
258 /**
259  * stat() wrapper.
260  *
261  * @param filename the filename to stat
262  * @param statbuf the stat info to fill in
263  * @param error return location for error
264  * @returns #FALSE if error was set
265  */
266 dbus_bool_t
267 _dbus_stat(const DBusString *filename,
268            DBusStat         *statbuf,
269            DBusError        *error)
270 {
271 #ifdef DBUS_WINCE
272         return TRUE;
273         //TODO
274 #else
275   const char *filename_c;
276 #if !defined(DBUS_WIN) && !defined(DBUS_WINCE)
277
278   struct stat sb;
279 #else
280
281   WIN32_FILE_ATTRIBUTE_DATA wfad;
282   char *lastdot;
283   DWORD rc;
284   PSID owner_sid, group_sid;
285   PSECURITY_DESCRIPTOR sd;
286 #endif
287
288   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
289
290   filename_c = _dbus_string_get_const_data (filename);
291
292   if (!GetFileAttributesEx (filename_c, GetFileExInfoStandard, &wfad))
293     {
294       _dbus_win_set_error_from_win_error (error, GetLastError ());
295       return FALSE;
296     }
297
298   if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
299     statbuf->mode = _S_IFDIR;
300   else
301     statbuf->mode = _S_IFREG;
302
303   statbuf->mode |= _S_IREAD;
304   if (wfad.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
305     statbuf->mode |= _S_IWRITE;
306
307   lastdot = strrchr (filename_c, '.');
308   if (lastdot && stricmp (lastdot, ".exe") == 0)
309     statbuf->mode |= _S_IEXEC;
310
311   statbuf->mode |= (statbuf->mode & 0700) >> 3;
312   statbuf->mode |= (statbuf->mode & 0700) >> 6;
313
314   statbuf->nlink = 1;
315
316   sd = NULL;
317   rc = GetNamedSecurityInfo ((char *) filename_c, SE_FILE_OBJECT,
318                              OWNER_SECURITY_INFORMATION |
319                              GROUP_SECURITY_INFORMATION,
320                              &owner_sid, &group_sid,
321                              NULL, NULL,
322                              &sd);
323   if (rc != ERROR_SUCCESS)
324     {
325       _dbus_win_set_error_from_win_error (error, rc);
326       if (sd != NULL)
327         LocalFree (sd);
328       return FALSE;
329     }
330
331   statbuf->uid = _dbus_win_sid_to_uid_t (owner_sid);
332   statbuf->gid = _dbus_win_sid_to_uid_t (group_sid);
333
334   LocalFree (sd);
335
336   statbuf->size = ((dbus_int64_t) wfad.nFileSizeHigh << 32) + wfad.nFileSizeLow;
337
338   statbuf->atime =
339     (((dbus_int64_t) wfad.ftLastAccessTime.dwHighDateTime << 32) +
340      wfad.ftLastAccessTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
341
342   statbuf->mtime =
343     (((dbus_int64_t) wfad.ftLastWriteTime.dwHighDateTime << 32) +
344      wfad.ftLastWriteTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
345
346   statbuf->ctime =
347     (((dbus_int64_t) wfad.ftCreationTime.dwHighDateTime << 32) +
348      wfad.ftCreationTime.dwLowDateTime) / 10000000 - DBUS_INT64_CONSTANT (116444736000000000);
349
350   return TRUE;
351 #endif //DBUS_WINCE
352 }
353
354
355 #ifdef HAVE_DIRENT_H
356
357 // mingw ships with dirent.h
358 #include <dirent.h>
359 #define _dbus_opendir opendir
360 #define _dbus_readdir readdir
361 #define _dbus_closedir closedir
362
363 #else
364
365 #ifdef HAVE_IO_H
366 #include <io.h> // win32 file functions
367 #endif
368
369 #include <sys/types.h>
370 #include <stdlib.h>
371
372 /* This file is part of the KDE project
373 Copyright (C) 2000 Werner Almesberger
374
375 libc/sys/linux/sys/dirent.h - Directory entry as returned by readdir
376
377 This program is free software; you can redistribute it and/or
378 modify it under the terms of the GNU Library General Public
379 License as published by the Free Software Foundation; either
380 version 2 of the License, or (at your option) any later version.
381
382 This program is distributed in the hope that it will be useful,
383 but WITHOUT ANY WARRANTY; without even the implied warranty of
384 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
385 Library General Public License for more details.
386
387 You should have received a copy of the GNU Library General Public License
388 along with this program; see the file COPYING.  If not, write to
389 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
390 Boston, MA 02110-1301, USA.
391 */
392 #define HAVE_NO_D_NAMLEN        /* no struct dirent->d_namlen */
393 #define HAVE_DD_LOCK            /* have locking mechanism */
394
395 #define MAXNAMLEN 255           /* sizeof(struct dirent.d_name)-1 */
396
397 #define __dirfd(dir) (dir)->dd_fd
398
399 /* struct dirent - same as Unix */
400 struct dirent
401   {
402     long d_ino;                    /* inode (always 1 in WIN32) */
403     off_t d_off;                /* offset to this dirent */
404     unsigned short d_reclen;    /* length of d_name */
405     char d_name[_MAX_FNAME+1];    /* filename (null terminated) */
406   };
407
408 /* typedef DIR - not the same as Unix */
409 typedef struct
410   {
411     long handle;                /* _findfirst/_findnext handle */
412     short offset;                /* offset into directory */
413     short finished;             /* 1 if there are not more files */
414     struct _finddata_t fileinfo;  /* from _findfirst/_findnext */
415     char *dir;                  /* the dir we are reading */
416     struct dirent dent;         /* the dirent to return */
417   }
418 DIR;
419
420 /**********************************************************************
421 * Implement dirent-style opendir/readdir/closedir on Window 95/NT
422 *
423 * Functions defined are opendir(), readdir() and closedir() with the
424 * same prototypes as the normal dirent.h implementation.
425 *
426 * Does not implement telldir(), seekdir(), rewinddir() or scandir().
427 * The dirent struct is compatible with Unix, except that d_ino is
428 * always 1 and d_off is made up as we go along.
429 *
430 * The DIR typedef is not compatible with Unix.
431 **********************************************************************/
432
433 DIR * _dbus_opendir(const char *dir)
434 {
435   DIR *dp;
436   char *filespec;
437   long handle;
438   int index;
439
440   filespec = malloc(strlen(dir) + 2 + 1);
441   strcpy(filespec, dir);
442   index = strlen(filespec) - 1;
443   if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
444     filespec[index] = '\0';
445   strcat(filespec, "\\*");
446
447   dp = (DIR *)malloc(sizeof(DIR));
448   dp->offset = 0;
449   dp->finished = 0;
450   dp->dir = strdup(dir);
451
452   if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0)
453     {
454       if (errno == ENOENT)
455         dp->finished = 1;
456       else
457         return NULL;
458     }
459
460   dp->handle = handle;
461   free(filespec);
462
463   return dp;
464 }
465
466 struct dirent * _dbus_readdir(DIR *dp)
467   {
468     if (!dp || dp->finished)
469       return NULL;
470
471     if (dp->offset != 0)
472       {
473         if (_findnext(dp->handle, &(dp->fileinfo)) < 0)
474           {
475             dp->finished = 1;
476             errno = 0;
477             return NULL;
478           }
479       }
480     dp->offset++;
481
482     strncpy(dp->dent.d_name, dp->fileinfo.name, _MAX_FNAME);
483     dp->dent.d_ino = 1;
484     dp->dent.d_reclen = strlen(dp->dent.d_name);
485     dp->dent.d_off = dp->offset;
486
487     return &(dp->dent);
488   }
489
490
491 int _dbus_closedir(DIR *dp)
492 {
493   if (!dp)
494     return 0;
495   _findclose(dp->handle);
496   if (dp->dir)
497     free(dp->dir);
498   if (dp)
499     free(dp);
500
501   return 0;
502 }
503
504 #endif //#ifdef HAVE_DIRENT_H
505
506 /**
507  * Internals of directory iterator
508  */
509 struct DBusDirIter
510   {
511     DIR *d; /**< The DIR* from opendir() */
512
513   };
514
515 /**
516  * Open a directory to iterate over.
517  *
518  * @param filename the directory name
519  * @param error exception return object or #NULL
520  * @returns new iterator, or #NULL on error
521  */
522 DBusDirIter*
523 _dbus_directory_open (const DBusString *filename,
524                       DBusError        *error)
525 {
526   DIR *d;
527   DBusDirIter *iter;
528   const char *filename_c;
529
530   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
531
532   filename_c = _dbus_string_get_const_data (filename);
533
534   d = _dbus_opendir (filename_c);
535   if (d == NULL)
536     {
537       dbus_set_error (error, _dbus_error_from_errno (errno),
538                       "Failed to read directory \"%s\": %s",
539                       filename_c,
540                       _dbus_strerror (errno));
541       return NULL;
542     }
543   iter = dbus_new0 (DBusDirIter, 1);
544   if (iter == NULL)
545     {
546       _dbus_closedir (d);
547       dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
548                       "Could not allocate memory for directory iterator");
549       return NULL;
550     }
551
552   iter->d = d;
553
554   return iter;
555 }
556
557 /**
558  * Get next file in the directory. Will not return "." or ".."  on
559  * UNIX. If an error occurs, the contents of "filename" are
560  * undefined. The error is never set if the function succeeds.
561  *
562  * @todo for thread safety, I think we have to use
563  * readdir_r(). (GLib has the same issue, should file a bug.)
564  *
565  * @param iter the iterator
566  * @param filename string to be set to the next file in the dir
567  * @param error return location for error
568  * @returns #TRUE if filename was filled in with a new filename
569  */
570 dbus_bool_t
571 _dbus_directory_get_next_file (DBusDirIter      *iter,
572                                DBusString       *filename,
573                                DBusError        *error)
574 {
575   struct dirent *ent;
576
577   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
578
579 again:
580   errno = 0;
581   ent = _dbus_readdir (iter->d);
582   if (ent == NULL)
583     {
584       if (errno != 0)
585         dbus_set_error (error,
586                         _dbus_error_from_errno (errno),
587                         "%s", _dbus_strerror (errno));
588       return FALSE;
589     }
590   else if (ent->d_name[0] == '.' &&
591            (ent->d_name[1] == '\0' ||
592             (ent->d_name[1] == '.' && ent->d_name[2] == '\0')))
593     goto again;
594   else
595     {
596       _dbus_string_set_length (filename, 0);
597       if (!_dbus_string_append (filename, ent->d_name))
598         {
599           dbus_set_error (error, DBUS_ERROR_NO_MEMORY,
600                           "No memory to read directory entry");
601           return FALSE;
602         }
603       else
604         return TRUE;
605     }
606 }
607
608 /**
609  * Closes a directory iteration.
610  */
611 void
612 _dbus_directory_close (DBusDirIter *iter)
613 {
614   _dbus_closedir (iter->d);
615   dbus_free (iter);
616 }
617
618 /**
619  * Checks whether the filename is an absolute path
620  *
621  * @param filename the filename
622  * @returns #TRUE if an absolute path
623  */
624 dbus_bool_t
625 _dbus_path_is_absolute (const DBusString *filename)
626 {
627   if (_dbus_string_get_length (filename) > 0)
628     return _dbus_string_get_byte (filename, 1) == ':'
629            || _dbus_string_get_byte (filename, 0) == '\\'
630            || _dbus_string_get_byte (filename, 0) == '/';
631   else
632     return FALSE;
633 }
634
635
636 static dbus_bool_t
637 fill_group_info(DBusGroupInfo    *info,
638                 dbus_gid_t        gid,
639                 const DBusString *groupname,
640                 DBusError        *error)
641 {
642   const char *group_c_str;
643
644   _dbus_assert (groupname != NULL || gid != DBUS_GID_UNSET);
645   _dbus_assert (groupname == NULL || gid == DBUS_GID_UNSET);
646
647   if (groupname)
648     group_c_str = _dbus_string_get_const_data (groupname);
649   else
650     group_c_str = NULL;
651
652   if (group_c_str)
653     {
654       PSID group_sid;
655       wchar_t *wgroupname = _dbus_win_utf8_to_utf16 (group_c_str, error);
656
657       if (!wgroupname)
658         return FALSE;
659
660       if (!_dbus_win_account_to_sid (wgroupname, &group_sid, error))
661         {
662           dbus_free (wgroupname);
663           return FALSE;
664         }
665
666       info->gid = _dbus_win_sid_to_uid_t (group_sid);
667       info->groupname = _dbus_strdup (group_c_str);
668
669       dbus_free (group_sid);
670       dbus_free (wgroupname);
671
672       return TRUE;
673     }
674   else
675     {
676       dbus_bool_t retval = FALSE;
677       wchar_t *wname, *wdomain;
678       char *name, *domain;
679
680       info->gid = gid;
681
682       if (!_dbus_win_sid_to_name_and_domain (gid, &wname, &wdomain, error))
683         return FALSE;
684
685       name = _dbus_win_utf16_to_utf8 (wname, error);
686       if (!name)
687         goto out0;
688
689       domain = _dbus_win_utf16_to_utf8 (wdomain, error);
690       if (!domain)
691         goto out1;
692
693       info->groupname = dbus_malloc (strlen (domain) + 1 + strlen (name) + 1);
694
695       strcpy (info->groupname, domain);
696       strcat (info->groupname, "\\");
697       strcat (info->groupname, name);
698
699       retval = TRUE;
700
701       dbus_free (domain);
702 out1:
703       dbus_free (name);
704 out0:
705       dbus_free (wname);
706       dbus_free (wdomain);
707
708       return retval;
709     }
710 }
711
712 /**
713  * Initializes the given DBusGroupInfo struct
714  * with information about the given group ID.
715  *
716  * @param info the group info struct
717  * @param gid group ID
718  * @param error the error return
719  * @returns #FALSE if error is set
720  */
721 dbus_bool_t
722 _dbus_group_info_fill_gid (DBusGroupInfo *info,
723                            dbus_gid_t     gid,
724                            DBusError     *error)
725 {
726   return fill_group_info (info, gid, NULL, error);
727 }
728
729 /**
730  * Initializes the given DBusGroupInfo struct
731  * with information about the given group name.
732  *
733  * @param info the group info struct
734  * @param groupname name of group
735  * @param error the error return
736  * @returns #FALSE if error is set
737  */
738 dbus_bool_t
739 _dbus_group_info_fill (DBusGroupInfo    *info,
740                        const DBusString *groupname,
741                        DBusError        *error)
742 {
743   return fill_group_info (info, DBUS_GID_UNSET,
744                           groupname, error);
745 }
746
747 /** @} */ /* End of DBusInternalsUtils functions */
748
749 /**
750  * @addtogroup DBusString
751  *
752  * @{
753  */
754 /**
755  * Get the directory name from a complete filename
756  * @param filename the filename
757  * @param dirname string to append directory name to
758  * @returns #FALSE if no memory
759  */
760 dbus_bool_t
761 _dbus_string_get_dirname(const DBusString *filename,
762                          DBusString       *dirname)
763 {
764   int sep;
765
766   _dbus_assert (filename != dirname);
767   _dbus_assert (filename != NULL);
768   _dbus_assert (dirname != NULL);
769
770   /* Ignore any separators on the end */
771   sep = _dbus_string_get_length (filename);
772   if (sep == 0)
773     return _dbus_string_append (dirname, "."); /* empty string passed in */
774
775   while (sep > 0 &&
776          (_dbus_string_get_byte (filename, sep - 1) == '/' ||
777           _dbus_string_get_byte (filename, sep - 1) == '\\'))
778     --sep;
779
780   _dbus_assert (sep >= 0);
781
782   if (sep == 0 ||
783       (sep == 2 &&
784        _dbus_string_get_byte (filename, 1) == ':' &&
785        isalpha (_dbus_string_get_byte (filename, 0))))
786     return _dbus_string_copy_len (filename, 0, sep + 1,
787                                   dirname, _dbus_string_get_length (dirname));
788
789   {
790     int sep1, sep2;
791     _dbus_string_find_byte_backward (filename, sep, '/', &sep1);
792     _dbus_string_find_byte_backward (filename, sep, '\\', &sep2);
793
794     sep = MAX (sep1, sep2);
795   }
796   if (sep < 0)
797     return _dbus_string_append (dirname, ".");
798
799   while (sep > 0 &&
800          (_dbus_string_get_byte (filename, sep - 1) == '/' ||
801           _dbus_string_get_byte (filename, sep - 1) == '\\'))
802     --sep;
803
804   _dbus_assert (sep >= 0);
805
806   if ((sep == 0 ||
807        (sep == 2 &&
808         _dbus_string_get_byte (filename, 1) == ':' &&
809         isalpha (_dbus_string_get_byte (filename, 0))))
810       &&
811       (_dbus_string_get_byte (filename, sep) == '/' ||
812        _dbus_string_get_byte (filename, sep) == '\\'))
813     return _dbus_string_copy_len (filename, 0, sep + 1,
814                                   dirname, _dbus_string_get_length (dirname));
815   else
816     return _dbus_string_copy_len (filename, 0, sep - 0,
817                                   dirname, _dbus_string_get_length (dirname));
818 }
819
820 /** @} */ /* DBusString stuff */
821