/* * This file Copyright (C) 2013-2014 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * */ #include /* bsearch() */ #include /* memcmp() */ #include "transmission.h" #include "ptrarray.h" #include "quark.h" #include "tr-assert.h" #include "utils.h" /* tr_memdup(), tr_strndup() */ struct tr_key_struct { char const* str; size_t len; }; static struct tr_key_struct const my_static[] = { { "", 0 }, { "activeTorrentCount", 18 }, { "activity-date", 13 }, { "activityDate", 12 }, { "added", 5 }, { "added-date", 10 }, { "added.f", 7 }, { "added6", 6 }, { "added6.f", 8 }, { "addedDate", 9 }, { "address", 7 }, { "alt-speed-down", 14 }, { "alt-speed-enabled", 17 }, { "alt-speed-time-begin", 20 }, { "alt-speed-time-day", 18 }, { "alt-speed-time-enabled", 22 }, { "alt-speed-time-end", 18 }, { "alt-speed-up", 12 }, { "announce", 8 }, { "announce-list", 13 }, { "announceState", 13 }, { "arguments", 9 }, { "bandwidth-priority", 18 }, { "bandwidthPriority", 17 }, { "bind-address-ipv4", 17 }, { "bind-address-ipv6", 17 }, { "bitfield", 8 }, { "blocklist-date", 14 }, { "blocklist-enabled", 17 }, { "blocklist-size", 14 }, { "blocklist-updates-enabled", 25 }, { "blocklist-url", 13 }, { "blocks", 6 }, { "bytesCompleted", 14 }, { "cache-size-mb", 13 }, { "clientIsChoked", 14 }, { "clientIsInterested", 18 }, { "clientName", 10 }, { "comment", 7 }, { "comment_utf_8", 13 }, { "compact-view", 12 }, { "complete", 8 }, { "config-dir", 10 }, { "cookies", 7 }, { "corrupt", 7 }, { "corruptEver", 11 }, { "created by", 10 }, { "created by.utf-8", 16 }, { "creation date", 13 }, { "creator", 7 }, { "cumulative-stats", 16 }, { "current-stats", 13 }, { "date", 4 }, { "dateCreated", 11 }, { "delete-local-data", 17 }, { "desiredAvailable", 16 }, { "destination", 11 }, { "dht-enabled", 11 }, { "display-name", 12 }, { "dnd", 3 }, { "done-date", 9 }, { "doneDate", 8 }, { "download-dir", 12 }, { "download-dir-free-space", 23 }, { "download-queue-enabled", 22 }, { "download-queue-size", 19 }, { "downloadCount", 13 }, { "downloadDir", 11 }, { "downloadLimit", 13 }, { "downloadLimited", 15 }, { "downloadSpeed", 13 }, { "downloaded", 10 }, { "downloaded-bytes", 16 }, { "downloadedBytes", 15 }, { "downloadedEver", 14 }, { "downloaders", 11 }, { "downloading-time-seconds", 24 }, { "dropped", 7 }, { "dropped6", 8 }, { "e", 1 }, { "encoding", 8 }, { "encryption", 10 }, { "error", 5 }, { "errorString", 11 }, { "eta", 3 }, { "etaIdle", 7 }, { "failure reason", 14 }, { "fields", 6 }, { "fileStats", 9 }, { "filename", 8 }, { "files", 5 }, { "files-added", 11 }, { "files-unwanted", 14 }, { "files-wanted", 12 }, { "filesAdded", 10 }, { "filter-mode", 11 }, { "filter-text", 11 }, { "filter-trackers", 15 }, { "flagStr", 7 }, { "flags", 5 }, { "fromCache", 9 }, { "fromDht", 7 }, { "fromIncoming", 12 }, { "fromLpd", 7 }, { "fromLtep", 8 }, { "fromPex", 7 }, { "fromTracker", 11 }, { "hasAnnounced", 12 }, { "hasScraped", 10 }, { "hashString", 10 }, { "have", 4 }, { "haveUnchecked", 13 }, { "haveValid", 9 }, { "honorsSessionLimits", 19 }, { "host", 4 }, { "id", 2 }, { "idle-limit", 10 }, { "idle-mode", 9 }, { "idle-seeding-limit", 18 }, { "idle-seeding-limit-enabled", 26 }, { "ids", 3 }, { "incomplete", 10 }, { "incomplete-dir", 14 }, { "incomplete-dir-enabled", 22 }, { "info", 4 }, { "info_hash", 9 }, { "inhibit-desktop-hibernation", 27 }, { "interval", 8 }, { "ip", 2 }, { "ipv4", 4 }, { "ipv6", 4 }, { "isBackup", 8 }, { "isDownloadingFrom", 17 }, { "isEncrypted", 11 }, { "isFinished", 10 }, { "isIncoming", 10 }, { "isPrivate", 9 }, { "isStalled", 9 }, { "isUTP", 5 }, { "isUploadingTo", 13 }, { "lastAnnouncePeerCount", 21 }, { "lastAnnounceResult", 18 }, { "lastAnnounceStartTime", 21 }, { "lastAnnounceSucceeded", 21 }, { "lastAnnounceTime", 16 }, { "lastAnnounceTimedOut", 20 }, { "lastScrapeResult", 16 }, { "lastScrapeStartTime", 19 }, { "lastScrapeSucceeded", 19 }, { "lastScrapeTime", 14 }, { "lastScrapeTimedOut", 18 }, { "leecherCount", 12 }, { "leftUntilDone", 13 }, { "length", 6 }, { "location", 8 }, { "lpd-enabled", 11 }, { "m", 1 }, { "magnet-info", 11 }, { "magnetLink", 10 }, { "main-window-height", 18 }, { "main-window-is-maximized", 24 }, { "main-window-layout-order", 24 }, { "main-window-width", 17 }, { "main-window-x", 13 }, { "main-window-y", 13 }, { "manualAnnounceTime", 18 }, { "max-peers", 9 }, { "maxConnectedPeers", 17 }, { "memory-bytes", 12 }, { "memory-units", 12 }, { "message-level", 13 }, { "metadataPercentComplete", 23 }, { "metadata_size", 13 }, { "metainfo", 8 }, { "method", 6 }, { "min interval", 12 }, { "min_request_interval", 20 }, { "move", 4 }, { "msg_type", 8 }, { "mtimes", 6 }, { "name", 4 }, { "name.utf-8", 10 }, { "nextAnnounceTime", 16 }, { "nextScrapeTime", 14 }, { "nodes", 5 }, { "nodes6", 6 }, { "open-dialog-dir", 15 }, { "p", 1 }, { "path", 4 }, { "path.utf-8", 10 }, { "paused", 6 }, { "pausedTorrentCount", 18 }, { "peer-congestion-algorithm", 25 }, { "peer-id-ttl-hours", 17 }, { "peer-limit", 10 }, { "peer-limit-global", 17 }, { "peer-limit-per-torrent", 22 }, { "peer-port", 9 }, { "peer-port-random-high", 21 }, { "peer-port-random-low", 20 }, { "peer-port-random-on-start", 25 }, { "peer-socket-tos", 15 }, { "peerIsChoked", 12 }, { "peerIsInterested", 16 }, { "peers", 5 }, { "peers2", 6 }, { "peers2-6", 8 }, { "peers6", 6 }, { "peersConnected", 14 }, { "peersFrom", 9 }, { "peersGettingFromUs", 18 }, { "peersSendingToUs", 16 }, { "percentDone", 11 }, { "pex-enabled", 11 }, { "piece", 5 }, { "piece length", 12 }, { "pieceCount", 10 }, { "pieceSize", 9 }, { "pieces", 6 }, { "play-download-complete-sound", 28 }, { "port", 4 }, { "port-forwarding-enabled", 23 }, { "port-is-open", 12 }, { "preallocation", 13 }, { "prefetch-enabled", 16 }, { "priorities", 10 }, { "priority", 8 }, { "priority-high", 13 }, { "priority-low", 12 }, { "priority-normal", 15 }, { "private", 7 }, { "progress", 8 }, { "prompt-before-exit", 18 }, { "queue-move-bottom", 17 }, { "queue-move-down", 15 }, { "queue-move-top", 14 }, { "queue-move-up", 13 }, { "queue-stalled-enabled", 21 }, { "queue-stalled-minutes", 21 }, { "queuePosition", 13 }, { "rateDownload", 12 }, { "rateToClient", 12 }, { "rateToPeer", 10 }, { "rateUpload", 10 }, { "ratio-limit", 11 }, { "ratio-limit-enabled", 19 }, { "ratio-mode", 10 }, { "recent-download-dir-1", 21 }, { "recent-download-dir-2", 21 }, { "recent-download-dir-3", 21 }, { "recent-download-dir-4", 21 }, { "recheckProgress", 15 }, { "remote-session-enabled", 22 }, { "remote-session-host", 19 }, { "remote-session-password", 23 }, { "remote-session-port", 19 }, { "remote-session-requres-authentication", 37 }, { "remote-session-username", 23 }, { "removed", 7 }, { "rename-partial-files", 20 }, { "reqq", 4 }, { "result", 6 }, { "rpc-authentication-required", 27 }, { "rpc-bind-address", 16 }, { "rpc-enabled", 11 }, { "rpc-host-whitelist", 18 }, { "rpc-host-whitelist-enabled", 26 }, { "rpc-password", 12 }, { "rpc-port", 8 }, { "rpc-url", 7 }, { "rpc-username", 12 }, { "rpc-version", 11 }, { "rpc-version-minimum", 19 }, { "rpc-whitelist", 13 }, { "rpc-whitelist-enabled", 21 }, { "scrape", 6 }, { "scrape-paused-torrents-enabled", 30 }, { "scrapeState", 11 }, { "script-torrent-done-enabled", 27 }, { "script-torrent-done-filename", 28 }, { "seconds-active", 14 }, { "secondsActive", 13 }, { "secondsDownloading", 18 }, { "secondsSeeding", 14 }, { "seed-queue-enabled", 18 }, { "seed-queue-size", 15 }, { "seedIdleLimit", 13 }, { "seedIdleMode", 12 }, { "seedRatioLimit", 14 }, { "seedRatioLimited", 16 }, { "seedRatioMode", 13 }, { "seederCount", 11 }, { "seeding-time-seconds", 20 }, { "session-count", 13 }, { "session-id", 10 }, { "sessionCount", 12 }, { "show-backup-trackers", 20 }, { "show-extra-peer-details", 23 }, { "show-filterbar", 14 }, { "show-notification-area-icon", 27 }, { "show-options-window", 19 }, { "show-statusbar", 14 }, { "show-toolbar", 12 }, { "show-tracker-scrapes", 20 }, { "size-bytes", 10 }, { "size-units", 10 }, { "sizeWhenDone", 12 }, { "sort-mode", 9 }, { "sort-reversed", 13 }, { "speed", 5 }, { "speed-Bps", 9 }, { "speed-bytes", 11 }, { "speed-limit-down", 16 }, { "speed-limit-down-enabled", 24 }, { "speed-limit-up", 14 }, { "speed-limit-up-enabled", 22 }, { "speed-units", 11 }, { "start-added-torrents", 20 }, { "start-minimized", 15 }, { "startDate", 9 }, { "status", 6 }, { "statusbar-stats", 15 }, { "tag", 3 }, { "tier", 4 }, { "time-checked", 12 }, { "torrent-added", 13 }, { "torrent-added-notification-command", 34 }, { "torrent-added-notification-enabled", 34 }, { "torrent-complete-notification-command", 37 }, { "torrent-complete-notification-enabled", 37 }, { "torrent-complete-sound-command", 30 }, { "torrent-complete-sound-enabled", 30 }, { "torrent-duplicate", 17 }, { "torrent-get", 11 }, { "torrent-set", 11 }, { "torrent-set-location", 20 }, { "torrentCount", 12 }, { "torrentFile", 11 }, { "torrents", 8 }, { "totalSize", 9 }, { "total_size", 10 }, { "tracker id", 10 }, { "trackerAdd", 10 }, { "trackerRemove", 13 }, { "trackerReplace", 14 }, { "trackerStats", 12 }, { "trackers", 8 }, { "trash-can-enabled", 17 }, { "trash-original-torrent-files", 28 }, { "umask", 5 }, { "units", 5 }, { "upload-slots-per-torrent", 24 }, { "uploadLimit", 11 }, { "uploadLimited", 13 }, { "uploadRatio", 11 }, { "uploadSpeed", 11 }, { "upload_only", 11 }, { "uploaded", 8 }, { "uploaded-bytes", 14 }, { "uploadedBytes", 13 }, { "uploadedEver", 12 }, { "url-list", 8 }, { "use-global-speed-limit", 22 }, { "use-speed-limit", 15 }, { "user-has-given-informed-consent", 31 }, { "ut_comment", 10 }, { "ut_holepunch", 12 }, { "ut_metadata", 11 }, { "ut_pex", 6 }, { "ut_recommend", 12 }, { "utp-enabled", 11 }, { "v", 1 }, { "version", 7 }, { "wanted", 6 }, { "warning message", 15 }, { "watch-dir", 9 }, { "watch-dir-enabled", 17 }, { "webseeds", 8 }, { "webseedsSendingToUs", 19 } }; static int compareKeys(void const* va, void const* vb) { int ret; struct tr_key_struct const* a = va; struct tr_key_struct const* b = vb; ret = memcmp(a->str, b->str, MIN(a->len, b->len)); if (ret == 0 && a->len != b->len) { ret = a->len < b->len ? -1 : 1; } return ret; } static tr_ptrArray my_runtime = TR_PTR_ARRAY_INIT_STATIC; bool tr_quark_lookup(void const* str, size_t len, tr_quark* setme) { static size_t const n_static = TR_N_ELEMENTS(my_static); TR_ASSERT(n_static == TR_N_KEYS); struct tr_key_struct tmp; struct tr_key_struct* match; bool success = false; tmp.str = str; tmp.len = len; /* is it in our static array? */ match = bsearch(&tmp, my_static, n_static, sizeof(struct tr_key_struct), compareKeys); if (match != NULL) { *setme = match - my_static; success = true; } /* was it added during runtime? */ if (!success && !tr_ptrArrayEmpty(&my_runtime)) { struct tr_key_struct** runtime = (struct tr_key_struct**)tr_ptrArrayBase(&my_runtime); size_t const n_runtime = tr_ptrArraySize(&my_runtime); for (size_t i = 0; i < n_runtime; ++i) { if (compareKeys(&tmp, runtime[i]) == 0) { *setme = TR_N_KEYS + i; success = true; break; } } } return success; } static tr_quark append_new_quark(void const* str, size_t len) { tr_quark ret; struct tr_key_struct* tmp; tmp = tr_new(struct tr_key_struct, 1); tmp->str = tr_strndup(str, len); tmp->len = len; ret = TR_N_KEYS + tr_ptrArraySize(&my_runtime); tr_ptrArrayAppend(&my_runtime, tmp); return ret; } tr_quark tr_quark_new(void const* str, size_t len) { tr_quark ret = TR_KEY_NONE; if (str == NULL) { goto finish; } if (len == TR_BAD_SIZE) { len = strlen(str); } if (!tr_quark_lookup(str, len, &ret)) { ret = append_new_quark(str, len); } finish: return ret; } char const* tr_quark_get_string(tr_quark q, size_t* len) { struct tr_key_struct const* tmp; if (q < TR_N_KEYS) { tmp = &my_static[q]; } else { tmp = tr_ptrArrayNth(&my_runtime, q - TR_N_KEYS); } if (len != NULL) { *len = tmp->len; } return tmp->str; }