Пример #1
0
    'search_subfolders') == "True" else False
working_path = t_dir
query = Tools.getArgv(1)
date_format = Tools.getEnv('date_format')

# Read file list deep and normal
files_in_directory = None
file_list = list()
if working_path:
    rf = RecentFiles(working_path)
    files_in_directory = rf.getRecentFilesDeep(
        reverse=True) if search_subfolders else rf.getRecentFiles(reverse=True)
    file_list = RecentFiles.search(query, files_in_directory) if bool(
        query) and files_in_directory else files_in_directory

wf = Items()

# When path not found, expose error to script filter
if files_in_directory is None:
    wf.setItem(
        title='Path "{0}" not found...'.format(t_dir),
        subtitle='Change path in List Filter, e.g. /Users/<user>/Desktop',
        valid=False)
    wf.setIcon('attention.png', 'png')
    wf.addItem()
# In case no files were found in directory
elif len(files_in_directory) == 0:
    wf.setItem(title='Folder is empty!',
               subtitle=u"\u23CE to start again",
               arg="*RESTART*",
               valid=True)
Пример #2
0
#!/usr/bin/env python
# encoding: utf-8

from Alfred import Items, Tools
from Notes import Search
from urllib import pathname2url
import os

items = Items()
query = Tools.getEnv("path_query2")
search = Search()
note_path = Tools.getEnv("path_query1")
note_title = search.getNoteTitle(note_path)
filename = pathname2url(os.path.basename(note_path))
back_query = "<EMPTY>" if not query else query

actions = [
    {
        "arg": "back|{0}".format(query),
        "icon": "icons/back.png",
        "subtitle": "Back to Search with query: {0}".format(back_query),
        "title": "Back",
    },
    {
        "arg": u"markdown_link|[{0}]({1})".format(note_title, filename),
        "icon": "icons/link.png",
        "subtitle": u"Copy a Markdown Link for \"{0}\" to the Clipboard".format(note_title),
        "title": "Markdown Link",
    },
    {
        "arg": u"wiki_link|[[{0}]]".format(note_title),
Пример #3
0
            ) else 'icons/question.png'
            items.setIcon(icon, 'image')
            items.addItem()


def write_config(key, val):
    value = Tools.normalize(val)
    Plist().setVariable(key, value)


query = Tools.getArgv(1)
action_key_value = Tools.getEnv('action_key_value')
[action, key,
 value] = action_key_value.split('|') if action_key_value != str() else [
     str(), str(), str()
 ]
workflow_dir = os.getcwd()
query = Tools.getArgv(1)

items = Items()

if action == str():
    print_config(query)
elif action == 'selection':
    get_selection(key, query)
else:
    write_config(key, value)
    print_config(query)

items.write()
Пример #4
0
# -*- coding: utf-8 -*-

from Alfred import Items, Tools
from MyNotes import Search

query = Tools.getArgv(1)
bmt = Tools.getEnv('bookmark_tag')
bookmark_tag = bmt if bmt.startswith('#') else '#' + bmt

search_terms = '{0}&{1}'.format(bookmark_tag, query) if query else bookmark_tag

notes = Search()
search_terms, _ = notes.get_search_config(search_terms)
matches = notes.url_search(search_terms)

alf = Items()
if matches:
    for m in matches:
        note_title = m.get('title')
        note_path = m.get('path')
        links = m.get('links')
        for l in links:
            url_title = l.get('url_title')
            url = l.get('url')
            # subtitle = '%s > %s' % (url_title, url) if url_title != url else url
            subtitle = 'NOTE: {0} URL: {1}...'.format(note_title, url[:30])
            alf.setItem(
                title=url_title,
                subtitle=subtitle,
                arg=url,
                quicklookurl=url
Пример #5
0
import urllib

import MyNotes
from Alfred import Items as Items
from Alfred import Tools as Tools

# create MD search object
md_search = MyNotes.Search()

# Load environment variables
ext = md_search.getNotesExtension()
# p = md_search.getNotesPath()
query = Tools.getArgv(1)
todos = md_search.todoSearch(query)

wf = Items()
if len(todos) > 0:
    for i in todos:
        md_path = urllib.pathname2url(i['path'])
        md_title = i['title'] if i['title'] != str() else Tools.chop(
            i['filename'], ext)
        wf.setItem(title=i['todo'],
                   subtitle=u'\u2192 {0} (Created: {1})'.format(
                       md_title.decode('utf-8'), Tools.getDateStr(i['ctime'])),
                   arg=i['path'],
                   valid=True,
                   type='file')
        wf.setIcon('icons/unchecked.png', 'image')
        # Mod for CMD - new action menu
        wf.addMod(key="cmd",
                  arg="{0}>{1}".format(i['path'], query),
Пример #6
0
#!/usr/bin/python

from Alfred import Items, Tools

target_dir = Tools.getEnv('directory')

wf = Items()
# Back Item
wf.setItem(title="BACK",
           subtitle="Back to List",
           arg='{0}|{1}'.format(target_dir, "BACK"))
wf.setIcon(m_path="back.png", m_type="image")
wf.addItem()

# Purge Dir Item
wf.setItem(title="Purge Directory",
           subtitle='Purge "{}"'.format(target_dir),
           arg='{0}|{1}'.format(target_dir, "PURGE"))
wf.setIcon(m_path="purge.png", m_type="image")
wf.addItem()

# Delete Item
wf.setItem(title="Remove Folder",
           subtitle='Remove "{}" from configuration'.format(target_dir),
           arg='{0}|{1}'.format(target_dir, "DELETE"))
wf.setIcon(m_path="delete.png", m_type="image")
wf.addItem()

wf.write()
Пример #7
0
    wf_dir_name = ''.join(
        [i for i in spath if str(i).startswith('user.workflow')])
    if wf_dir_name != str():
        target_file = target_dir + '/' + wf_dir_name + '.md'
        with open(target_file, "w+") as f:
            f.write(content)
            return target_file


Workflows = Workflows()
query = Tools.getArgv(1)
matches = Workflows.get_workflows() if query == str(
) else Workflows.search_in_workflows(query)

clean_cache()
alf = Items()
if len(matches) > 0:
    for m in matches:
        kf = KeywordFormatter()
        description = m.get('description') if m.get('description') else ' - '
        name = m.get('name')
        keyword_list = m.get('keywords')
        info_plist_path = m.get('path')
        wf_path = os.path.dirname(info_plist_path)
        for kitem in keyword_list:
            keyword = kitem.get('keyword')
            text = kitem.get('text') if kitem.get('text') else str()
            title = kitem.get('title') if kitem.get('title') else text
            kf.add_keyword_title(keyword, title)

        content = (("# %s\n"
Пример #8
0
from MyNotes import Search

# create MD search object
md_search = Search()

# Load Env variables
query = Tools.getArgv(1)
# TODO: For testing, delete 3 lines
#query = "Äsch".encode('utf-8')
# sys.stderr.write(query)

# Get Search config with AND and OR
search_terms, search_type = md_search.get_search_config(query)

# create WF object
wf = Items()

# exec search if search terms were entered
if len(search_terms) > 0:
    sorted_file_list = md_search.notes_search(search_terms, search_type)
# get full list of file in case no search was entered
else:
    sorted_file_list = md_search.getFilesListSorted()
# Write search results into WF object
for f in sorted_file_list:
    c_date = Tools.getDateStr(f['ctime'])
    m_date = Tools.getDateStr(f['mtime'])
    wf.setItem(
        title=f['title'],
        subtitle=
        u"Created: {0}, Modified: {1} ({2} \u2192 Actions, {3} \u2192 Quicklook)"
                os.mkdir(target_dir)
        spath = os.path.normpath(wf_dir).split(os.sep)
        wf_dir_name = ''.join([i for i in spath if str(i).startswith('user.workflow')])
        if wf_dir_name != str():
                target_file = target_dir + '/' + wf_dir_name + '.md'
                if os.path.isfile(target_file):
                        os.remove(target_file)
                with open(target_file, "w+") as f:
                        f.write(content)
                        return target_file

INPUT_TYPES = [
        'alfred.workflow.input.scriptfilter',
        'alfred.workflow.input.keyword'
        ]
alf = Items()
exclude_disabled = Tools.getEnv('exclude_disabled').lower()
exclude_disabled = True if exclude_disabled == "true" else False
workflow_plist_lists =  get_workflow_plist_paths()
for wf in workflow_plist_lists:
        plist_config = get_plist_info(wf)
        if exclude_disabled and plist_config['disabled']:
                continue
        wf_object = plist_config['objects']
        kf = KeywordFormatter()
        for o in wf_object:
                item_type = o.get('type')
                if item_type in INPUT_TYPES:
                        item_config = o.get('config')
                        keyword = item_config.get('keyword')
                        title = item_config.get('title')
Пример #10
0
#!/usr/bin/python

from Alfred import Items

# Script Filter icon [Title,Subtitle,arg/uid/icon]
wf_items = [['Open Workflow', 'Open Workflow in Alfred Preferences', 'open'],
            ['Copy Clipboard', 'Copy Workflow path to Cliboard', 'clipboard'],
            ['Open in Terminal', 'Open Workflow path in Terminal', 'terminal'],
            ['Finder', 'Reveal in Finder', 'finder'],
            ['Forklift', 'Reveal in Forkflift', 'forklift']]

wf = Items()
for w in wf_items:
    wf.setItem(title=w[0], subtitle=w[1], arg=w[2], uid=w[2])
    icon_path = 'icons/' + w[2] + '.png'
    wf.setIcon(icon_path, m_type='image')
    wf.addItem()

wf.write()
Пример #11
0
SUFFIX = " (DEFAULT)"

# create MD search object
my_notes = MyNotes.Search()

# Load env variables
ext = my_notes.getNotesExtension()
p = my_notes.getNotesPath()
query = Tools.getArgv(1)
default_template = Tools.getEnv('default_template')
template_tag = get_template_tag()

# Get Files sorted in Notes directory
all_files = my_notes.getFilesListSorted()
template_files = sorted(all_files, key=lambda x: x['filename'] != default_template)

wf = Items()
for md_file in template_files:
    if my_notes.isNoteTagged(md_file['path'], template_tag) and query in md_file['filename']:
        suffix = str()
        if md_file['filename'] == default_template:
            suffix = SUFFIX
        wf.setItem(
            title=md_file['filename'] + suffix,
            subtitle="Create new file based on \"{0}\"".format(md_file['filename']),
            arg=Tools.strJoin(md_file['path']),
            type='file'
        )
        wf.addItem()
wf.write()
Пример #12
0
#!/usr/bin/python
from Alfred import Items, Tools
from Workflows import Workflows

wf = Workflows()
alf = Items()
wpath = Tools.getEnv('plist_path') + "/info.plist"

keyword_list = wf.get_item(wpath).get('keywords')
if keyword_list:
    for k in keyword_list:
        withspace = k.get('withspace')
        keyw = k.get('keyword')
        keyword = '{0} '.format(keyw) if withspace and keyw else keyw
        title = k.get('title')
        text = k.get('text')
        if keyword:
            alf.setItem(title=title,
                        subtitle=u'Press \u23CE to proceed with Keyword: %s' %
                        keyword,
                        arg=keyword)
            alf.setIcon('icons/start.png', m_type='image')
            alf.addItem()
else:
    alf.setItem(title="This workflow has not keywords defined", valid=False)
alf.write()
from Alfred import Items as Items
from Alfred import Tools as Tools
import MyNotes
import urllib

# create MD search object
md_search = MyNotes.Search()

# Load environment variables
ext = md_search.getNotesExtension()
p = md_search.getNotesPath()
query = Tools.getArgv(1)
todos = md_search.todoSearch(query)

wf = Items()
if len(todos) > 0:
    for i in todos:
        md_path = urllib.pathname2url(i['path'])
        md_title = i['title'] if i['title'] != str() else Tools.chop(i['filename'], ext)
        wf.setItem(
            title=i['todo'],
            subtitle=u'\u2192 %s (Created: %s)' % (md_title.decode('utf-8'), Tools.getDateStr(i['ctime'])),
            arg=i['path'],
            valid=True,
            type='file'
        )
        wf.setIcon('icons/unchecked.png','image')
        # Mod for CTRL - delete a Note
        wf.addMod(
            key="ctrl",
Пример #14
0
    """
    config = Plist()
    config.setVariable(key, value)
    value = '<EMPTY>' if value == str() else value
    wf.setItem(title="Proceed?",
               subtitle='%s will be changed to: %s' % (key, value),
               arg='')
    wf.setIcon('icons/hand.png', 'image')
    wf.addItem()


query = Tools.getArgv(1)
action_key_value = Tools.getEnv('action_key_value')
[action, key,
 value] = action_key_value.split('|') if action_key_value != str() else [
     str(), str(), str()
 ]
wf_dir = os.getcwd()
query = Tools.getArgv(1)

wf = Items()

if action == str():
    print_config(query)
elif action == 'selection':
    get_selection(key, query)
else:
    write_config(key, value)

wf.write()
# create MD search object
md = MyNotes.Search()

# Get environment variables
ext = md.getNotesExtension()
p = md.getNotesPath()
query = Tools.getArgv(1)

if query is str():
    # Tag Search and sort based on tag name
    tag_results = md.tagSearch(query, 'tag', reverse=False)
else:
    # Tag Search and sort based on number of Hits
    tag_results = md.tagSearch(query, 'count', reverse=True)

wf = Items()

if bool(tag_results):
    for tag, counter in tag_results.items():
        wf.setItem(
            title='%s' % tag,
            subtitle="%s Hit(s)" % counter,
            valid=True,
            arg='#%s' % tag
        )
        wf.setIcon('icons/hashtag.png', 'image')
        wf.addItem()
else:
    wf.setItem(
        title="No Tags found!",
        subtitle="No Tags matches search term",
    return s_terms, s_type


# create MD search object
md_search = MyNotes.Search()

# Load Env variables
ext = md_search.getNotesExtension()
p = md_search.getNotesPath()
query = Tools.getArgv(1)

# Get Search config with AND and OR
search_terms, search_type = get_search_config(query)

# create WF object
wf = Items()

# run search of search term(s)
if len(search_terms) > 0:
    search_results_list = list()
    sorted_file_list = md_search.searchInFiles(search_terms, search_type)
    search_results_list.extend(sorted_file_list)

    # Write search results into WF object

    for f in search_results_list:
        c_date = Tools.getDateStr(f['ctime'])
        m_date = Tools.getDateStr(f['mtime'])
        # md_path = urllib.pathname2url(f['filename'])
        md_path = f['filename'].replace(' ', '%20')
        url_scheme_path = md_search.getUrlScheme(f['path'])
Пример #17
0
#!/usr/bin/python

import json
import os

from Alfred import Items, Tools

query = Tools.getArgv(1)
wf_data_dir = Tools.getDataDir()
config_file = os.path.join(wf_data_dir, "folders.json")
wf = Items()
if os.path.isfile(config_file):
    with open(config_file, "r") as f:
        config = json.load(f)
    # List Folders from folders.json
    if len(config.keys()) > 0:
        for k, v in config.items():
            if os.path.isdir(v) and (query == str()
                                     or k.lower().startswith(query.lower())):
                wf.setItem(
                    title=k,
                    subtitle=
                    u"\u23CE to list Files or \u2318 for addtional actions",
                    arg=v)
                wf.addMod(key="cmd", arg=v, subtitle="Enter Action Menu")
                wf.addModsToItem()
            elif not (os.path.isdir(v)):
                wf.setItem(title=k, subtitle="Folder not found!", arg=k)
                wf.setIcon("attention.png", "image")
            wf.addItem()
    else:
Пример #18
0
#!/usr/bin/env python
# encoding: utf-8

from Alfred import Items, Tools
from Notes import Search

items = Items()
query = Tools.getArgv(1)
search = Search()

tasks = search.tasks(query)

if len(tasks) > 0:
    for file in tasks:
        file_title = file['title'] if file['title'] != str(
        ) else search.getNoteFilename(file['path'])
        items.setItem(
            arg=file['path'],
            subtitle=
            u'\u2192 {0} (\u2318 Open in The Archive, \u2325 Open in Default Editor)'
            .format(file['filename'].decode('utf-8')),
            title=file['todo'],
            type='file',
            valid=True,
            variables={
                "todo": file['todo'],
                "todo_query": query,
                "todo_status": file['status'],
            },
        )
        items.addMod(
        "subtitle": "Copy Url Scheme as Markdown Link to Clipboard",
        "arg": "{0}|{1}".format("urlscheme", note_path),
        "icon": "icons/scheme.png",
        "visible": Tools.getEnv("url_scheme")
    },
    {
        "title":
        "Delete Note",
        "subtitle":
        u"Delete \"{0}\". This action cannot be undone!".format(note_title),
        "arg":
        "{0}|{1}>{2}".format("delete", note_path, query),
        "icon":
        "icons/delete.png",
        "visible":
        True
    },
]

# Generate ScriptFilter Output
wf = Items()
for a in ACTIONS:
    val = a.get('visible')
    if val:
        wf.setItem(title=a.get("title"),
                   subtitle=a.get("subtitle"),
                   arg=a.get("arg"))
        wf.setIcon(m_path=a.get("icon"), m_type="image")
        wf.addItem()
wf.write()
Пример #20
0
#!/usr/bin/env python
# encoding: utf-8

from Alfred import Items, Tools
from Notes import Search

items = Items()
query = Tools.getArgv(1)
search = Search()

if query is str():
    tag_results = search.tags(query, 'tag', reverse=False)
else:
    tag_results = search.tags(query, 'count', reverse=True)

if bool(tag_results):
    for tag, counter in tag_results.items():
        items.setItem(
            arg=
            tag,  # we cannot yet send a hash character with `thearchive://match/{query}`
            subtitle=u"{0} Hit(s), (\u2318 Paste Into Frontmost Application)".
            format(counter),
            title=tag,
            valid=True,
        )
        items.setIcon('icons/hashtag.png', 'image')
        items.addMod(
            arg=u"#{0} ".format(tag),
            icon_path='icons/paste.png',
            icon_type='image',
            key='cmd',
sys.stderr.write(platform.python_version())

f_path = Tools.getEnv('path')
ws_home = f_path if f_path else os.path.expanduser(
    Tools.getEnv('workspaces_home'))
p_path = str(
    PurePath(f_path).parent
) if f_path and f_path != Tools.getEnv('workspaces_home') else str()

query = Tools.getArgv(1)
if query == str():
    it = sorted(get_dirs(ws_home))
else:
    it = sorted(get_files(ws_home))

wf = Items()
if p_path:
    wf.setItem(title='Back', arg=p_path)
    wf.setIcon(m_path='back.png', m_type='image')
    wf.addItem()
if len(it) > 0:
    for i in it:
        if (query == str() or query.lower() in PurePath(i).stem.lower()
            ) and not (os.path.basename(i).startswith('.')):
            ic = 'folder.png' if os.path.isdir(i) else 'workspace.png'
            sub = 'Folder' if os.path.isdir(i) else "Workspace in VSCode"
            title = os.path.basename(i).replace('.code-workspace', '')
            wf.setItem(title=title,
                       subtitle=u'\u23CE to open {0}'.format(sub),
                       arg=i)
            wf.setIcon(m_path=ic, m_type='image')
Пример #22
0
    Args:
        pass_lib (str): Path to 1Password libraries including user roort

    Returns:
        list(dict): list of dictonaries contain all Passwords
    """
    passwords = list()
    for r, d, f in os.walk(pass_lib):
        for file in f:
            file_path = os.path.join(r, file)
            with open(file_path, 'r') as content:
                passwords.append(json.load(content))
    return passwords


wf = Items()
# Inform user if 3rd Party integration is disabled
# If 3rd party integration is enabled, read all passwords items
if os.path.isdir(pass_lib):
    passwords = get_passwords(pass_lib)
else:
    wf.setItem(
        title="3rd Party Integration in 1Password is disabled!",
        subtitle=
        u'1Password > Preferences > Advanced > Integratons > [x] Enable Spotlight and 3rd party app integrations',
        valid=False)
    wf.addItem()
    passwords = list()

for p in passwords:
    # Add Password item if no vaultNames are defined OR item's vault name in valutName config
Пример #23
0
# create MD search object
md = Search()

# Get environment variables
ext = md.getNotesExtension()
# p = md.getNotesPath()
query = Tools.getArgv(1)

if query is str():
    # Tag Search and sort based on tag name
    tag_results = md.tagSearch(query, 'tag', reverse=False)
else:
    # Tag Search and sort based on number of Hits
    tag_results = md.tagSearch(query, 'count', reverse=True)

wf = Items()

if bool(tag_results):
    for tag, counter in tag_results.items():
        wf.setItem(
            title='{0}'.format(tag),
            subtitle=u"{0} Hit(s), (\u2318 to paste tag into frontmost app)".
            format(counter),
            valid=True,
            arg='#{0}'.format(tag))
        wf.setIcon('icons/hashtag.png', 'image')
        wf.addMod(key='cmd',
                  arg='#{0} '.format(tag),
                  subtitle='Paste Tag into frontmost app',
                  icon_path='icons/paste.png',
                  icon_type='image')
Пример #24
0
def path_to_bookmarks():
    user_dir = os.path.expanduser('~')
    bm = user_dir + BRAVE_BOOKMARKS
    bm_dev = user_dir + BRAVE_DEV_BOOKMARKS
    if os.path.isfile(bm):
        return bm
    elif os.path.isfile(bm_dev):
        return bm_dev


def get_json_from_file(file):
    return json.load(codecs.open(file, 'r', 'utf-8-sig'))['roots']


wf = Items()
query = Tools.getArgv(1) if Tools.getArgv(1) is not None else str()
bookmarks_file = path_to_bookmarks()

if bookmarks_file is not None:
    bm_json = get_json_from_file(bookmarks_file)
    bookmarks = get_all_urls(bm_json)
    for bm in bookmarks:
        name = bm.get('name')
        url = bm.get('url')
        if query == str() or query.lower() in name.lower():
            wf.setItem(title=name, subtitle=url, arg=url, quicklookurl=url)
            wf.addItem()

    if wf.getItemsLengths() == 0:
        wf.setItem(title='No Bookmark found!',
Пример #25
0
#!/usr/bin/env python
# encoding: utf-8

from Alfred import Items, Tools
from Notes import Search

items = Items()
query = Tools.getArgv(1)
search = Search()
search_terms, search_type = search.getSearchConfig(query)

if len(search_terms) > 0:
    sorted_file_list = search.notes(search_terms, search_type)
else:
    sorted_file_list = search.getFilesListSorted()

for file in sorted_file_list:
    c_date = Tools.getDateStr(file['ctime'])
    m_date = Tools.getDateStr(file['mtime'])
    items.setItem(
        arg=file['path'],
        subtitle=u"Created: {0}, Modified: {1} (\u2318 Actions, \u2325 Paste Wiki Link, \u21E7 Quicklook)".format(c_date, m_date),
        title=file['title'],
        type="file",
    )
    items.addMod(
        arg="{0}|>{1}".format(file['path'], query),
        icon_path="icons/action.png",
        icon_type="image",
        key="cmd",
        subtitle="Enter Actions Menu for the Note...",
Пример #26
0
#!/usr/bin/env python
# encoding: utf-8

from Alfred import Items, Tools
from Notes import Notes, Search

items = Items()
notes = Notes()
search = Search()
zettel_id = Tools.getZettelId()
query = Tools.getArgv(1)
query_alt = u"{0} {1}".format(zettel_id, query)
zettel_action = u"\u2318 Add Zettel ID"
paste_action = u"\u2325 Paste clipboard"
quicklook_action = u"\u21E7 Quicklook"

if notes.useZettelId():
    query_alt = query
    query = u"{0} {1}".format(zettel_id, query_alt)
    zettel_action = u"\u2318 Remove Zettel ID"

items.setItem(
    arg=u"{0}||".format(query),
    subtitle=u"\"{0}\" ({1}, {2})".format(query, zettel_action, paste_action),
    title="Create note",
)
items.addMod(
    arg=u"{0}||".format(query_alt),
    key="cmd",
    subtitle=u"\"{0}\" ({1})".format(query_alt, paste_action),
)
Пример #27
0
            if (term.lower() in i[1].lower()) or (term.lower()
                                                  in i[0].lower()):
                newList.append(i)
    else:
        newList = li
    return newList[:50]


def path_to_history():
    user_dir = os.path.expanduser('~')
    bm = user_dir + BRAVE_HISTORY
    bm_dev = user_dir + BRAVE_DEV_HISTORY
    return bm if os.path.isfile(bm) else bm_dev


wf = Items()

search_term = Tools.getArgv(1) if Tools.getArgv(1) is not None else ''
chrome_locked_db = path_to_history()
history_db = '/tmp/History'

try:
    shutil.copy2(chrome_locked_db, '/tmp')
except IOError:
    wf.setItem(title="Brave Browser History not found!",
               subtitle="You may use an older version of Brave or no Brave",
               valid=False)
    wf.addItem()
    wf.write()
    exit()