Revise correction of typos.

This commit is contained in:
Masahiko Sawada 2015-12-11 11:39:52 +05:30
parent 2fe6a7b4f5
commit a8249a2948
24 changed files with 65 additions and 65 deletions

View File

@ -263,7 +263,7 @@ int startAppendOnly(void) {
* About the 'force' argument:
*
* When the fsync policy is set to 'everysec' we may delay the flush if there
* is still a fsync() going on in the background thread, since for instance
* is still an fsync() going on in the background thread, since for instance
* on Linux write(2) will be blocked by the background fsync anyway.
* When this happens we remember that there is some aof buffer to be
* flushed ASAP, and will try to do that in the serverCron() function.

View File

@ -1072,7 +1072,7 @@ void clusterHandleConfigEpochCollision(clusterNode *sender) {
* in the cluster without dealing with the problem of other nodes re-adding
* back the node to nodes we already sent the FORGET command to.
*
* The data structure used is a hash table with a sds string representing
* The data structure used is a hash table with an sds string representing
* the node ID as key, and the time when it is ok to re-add the node as
* value.
* -------------------------------------------------------------------------- */
@ -1117,7 +1117,7 @@ void clusterBlacklistAddNode(clusterNode *node) {
}
/* Return non-zero if the specified node ID exists in the blacklist.
* You don't need to pass a sds string here, any pointer to 40 bytes
* You don't need to pass an sds string here, any pointer to 40 bytes
* will work. */
int clusterBlacklistExists(char *nodeid) {
sds id = sdsnewlen(nodeid,CLUSTER_NAMELEN);
@ -3634,7 +3634,7 @@ sds representClusterNodeFlags(sds ci, uint16_t flags) {
/* Generate a csv-alike representation of the specified cluster node.
* See clusterGenNodesDescription() top comment for more information.
*
* The function returns the string representation as a SDS string. */
* The function returns the string representation as an SDS string. */
sds clusterGenNodeDescription(clusterNode *node) {
int j, start;
sds ci;
@ -3700,7 +3700,7 @@ sds clusterGenNodeDescription(clusterNode *node) {
}
/* Generate a csv-alike representation of the nodes we are aware of,
* including the "myself" node, and return a SDS string containing the
* including the "myself" node, and return an SDS string containing the
* representation (it is up to the caller to free it).
*
* All the nodes matching at least one of the node flags specified in
@ -4327,7 +4327,7 @@ void createDumpPayload(rio *payload, robj *o) {
unsigned char buf[2];
uint64_t crc;
/* Serialize the object in a RDB-like format. It consist of an object type
/* Serialize the object in an RDB-like format. It consist of an object type
* byte followed by the serialized object. This is understood by RESTORE. */
rioInitWithBuffer(payload,sdsempty());
serverAssert(rdbSaveObjectType(payload,o));

View File

@ -225,7 +225,7 @@ int dbDelete(redisDb *db, robj *key) {
* o = dbUnshareStringValue(db,key,o);
*
* At this point the caller is ready to modify the object, for example
* using a sdscat() call to append some data, or anything else.
* using an sdscat() call to append some data, or anything else.
*/
robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) {
serverAssert(o->type == OBJ_STRING);

View File

@ -367,7 +367,7 @@ void debugCommand(client *c) {
key = dictGetKey(de);
if (val->type != OBJ_STRING || !sdsEncodedObject(val)) {
addReplyError(c,"Not a sds encoded string.");
addReplyError(c,"Not an sds encoded string.");
} else {
addReplyStatusFormat(c,
"key_sds_len:%lld, key_sds_avail:%lld, "

View File

@ -188,7 +188,7 @@ int _dictInit(dict *d, dictType *type,
}
/* Resize the table to the minimal size that contains all the elements,
* but with the invariant of an USED/BUCKETS ratio near to <= 1 */
* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
int dictResize(dict *d)
{
int minimal;

View File

@ -304,7 +304,7 @@ struct commandHelp {
"1.0.0" },
{ "EXPIREAT",
"key timestamp",
"Set the expiration for a key as an UNIX timestamp",
"Set the expiration for a key as a UNIX timestamp",
0,
"1.2.0" },
{ "FLUSHALL",
@ -574,7 +574,7 @@ struct commandHelp {
"2.6.0" },
{ "PEXPIREAT",
"key milliseconds-timestamp",
"Set the expiration for a key as an UNIX timestamp specified in milliseconds",
"Set the expiration for a key as a UNIX timestamp specified in milliseconds",
0,
"2.6.0" },
{ "PFADD",

View File

@ -750,10 +750,10 @@ int hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) {
*
* The other cases are more complex: our register requires to be updated
* and is either currently represented by a VAL opcode with len > 1,
* by a ZERO opcode with len > 1, or by a XZERO opcode.
* by a ZERO opcode with len > 1, or by an XZERO opcode.
*
* In those cases the original opcode must be split into muliple
* opcodes. The worst case is a XZERO split in the middle resuling into
* opcodes. The worst case is an XZERO split in the middle resuling into
* XZERO - VAL - XZERO, so the resulting sequence max length is
* 5 bytes.
*
@ -1084,7 +1084,7 @@ int hllMerge(uint8_t *max, robj *hll) {
/* ========================== HyperLogLog commands ========================== */
/* Create a HLL object. We always create the HLL using sparse encoding.
/* Create an HLL object. We always create the HLL using sparse encoding.
* This will be upgraded to the dense representation as needed. */
robj *createHLLObject(void) {
robj *o;
@ -1205,7 +1205,7 @@ void pfcountCommand(client *c) {
uint8_t max[HLL_HDR_SIZE+HLL_REGISTERS], *registers;
int j;
/* Compute a HLL with M[i] = MAX(M[i]_j). */
/* Compute an HLL with M[i] = MAX(M[i]_j). */
memset(max,0,sizeof(max));
hdr = (struct hllhdr*) max;
hdr->encoding = HLL_RAW; /* Special internal-only encoding. */
@ -1287,7 +1287,7 @@ void pfmergeCommand(client *c) {
struct hllhdr *hdr;
int j;
/* Compute a HLL with M[i] = MAX(M[i]_j).
/* Compute an HLL with M[i] = MAX(M[i]_j).
* We we the maximum into the max array of registers. We'll write
* it to the target variable later. */
memset(max,0,sizeof(max));

View File

@ -224,7 +224,7 @@ sds createLatencyReport(void) {
int advise_data_writeback = 0; /* data=writeback. */
int advise_no_appendfsync = 0; /* don't fsync during rewrites. */
int advise_local_disk = 0; /* Avoid remote disks. */
int advise_ssd = 0; /* Use a SSD drive. */
int advise_ssd = 0; /* Use an SSD drive. */
int advise_write_load_info = 0; /* Print info about AOF and write load. */
int advise_hz = 0; /* Use higher HZ. */
int advise_large_objects = 0; /* Deletion of large objects. */
@ -439,7 +439,7 @@ sds createLatencyReport(void) {
}
if (advise_no_appendfsync) {
report = sdscat(report,"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing a RDB file (the moment where there is high disk contention).\n");
report = sdscat(report,"- Assuming from the point of view of data safety this is viable in your environment, you could try to enable the 'no-appendfsync-on-rewrite' option, so that fsync will not be performed while there is a child rewriting the AOF file or producing an RDB file (the moment where there is high disk contention).\n");
}
if (advise_relax_fsync_policy && server.aof_fsync == AOF_FSYNC_ALWAYS) {

View File

@ -1629,7 +1629,7 @@ void rewriteClientCommandArgument(client *c, int i, robj *newval) {
* enforcing the client output length limits. */
unsigned long getClientOutputBufferMemoryUsage(client *c) {
unsigned long list_item_size = sizeof(listNode)+5;
/* The +5 above means we assume a sds16 hdr, may not be true
/* The +5 above means we assume an sds16 hdr, may not be true
* but is not going to be a problem. */
return c->reply_bytes + (list_item_size*listLength(c->reply));

View File

@ -62,7 +62,7 @@ int keyspaceEventsStringToFlags(char *classes) {
/* This function does exactly the revese of the function above: it gets
* as input an integer with the xored flags and returns a string representing
* the selected classes. The string returned is a sds string that needs to
* the selected classes. The string returned is an sds string that needs to
* be released with sdsfree(). */
sds keyspaceEventsFlagsToString(int flags) {
sds res;

View File

@ -121,7 +121,7 @@ int rdbSaveLen(rio *rdb, uint32_t len) {
}
/* Load an encoded length. The "isencoded" argument is set to 1 if the length
* is not actually a length but a "encoding type". See the RDB_ENC_*
* is not actually a length but an "encoding type". See the RDB_ENC_*
* definitions in rdb.h for more information. */
uint32_t rdbLoadLen(rio *rdb, int *isencoded) {
unsigned char buf[2];
@ -216,7 +216,7 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags) {
}
/* String objects in the form "2391" "-100" without any space and with a
* range of values that can fit in a 8, 16 or 32 bit signed value can be
* range of values that can fit in an 8, 16 or 32 bit signed value can be
* encoded as integers to save space */
int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {
long long value;
@ -277,7 +277,7 @@ ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
return nwritten;
}
/* Load a LZF compressed string in RDB format. The returned value
/* Load an LZF compressed string in RDB format. The returned value
* changes according to 'flags'. For more info check the
* rdbGenericLoadStringObject() function. */
void *rdbLoadLzfStringObject(rio *rdb, int flags) {
@ -382,16 +382,16 @@ int rdbSaveStringObject(rio *rdb, robj *obj) {
}
}
/* Load a string object from a RDB file according to flags:
/* Load a string object from an RDB file according to flags:
*
* RDB_LOAD_NONE (no flags): load a RDB object, unencoded.
* RDB_LOAD_NONE (no flags): load an RDB object, unencoded.
* RDB_LOAD_ENC: If the returned type is a Redis object, try to
* encode it in a special way to be more memory
* efficient. When this flag is passed the function
* no longer guarantees that obj->ptr is a SDS string.
* no longer guarantees that obj->ptr is an SDS string.
* RDB_LOAD_PLAIN: Return a plain string allocated with zmalloc()
* instead of a Redis object.
* RDB_LOAD_SDS: Return a SDS string instead of a Redis object.
* RDB_LOAD_SDS: Return an SDS string instead of a Redis object.
*/
void *rdbGenericLoadStringObject(rio *rdb, int flags) {
int encode = flags & RDB_LOAD_ENC;
@ -1355,7 +1355,7 @@ int rdbLoad(char *filename) {
/* Read value */
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
/* Check if the key already expired. This function is used when loading
* a RDB file from disk, either at startup, or when a RDB was
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave. */
@ -1541,7 +1541,7 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
}
}
/* Spawn a RDB child that writes the RDB to the sockets of the slaves
/* Spawn an RDB child that writes the RDB to the sockets of the slaves
* that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */
int rdbSaveToSlavesSockets(void) {
int *fds;

View File

@ -71,7 +71,7 @@ static errors_t errors;
errors.level++; \
}
/* Data type to hold opcode with optional key name a success status */
/* Data type to hold opcode with optional key name an success status */
typedef struct {
char* key;
int type;

View File

@ -491,7 +491,7 @@ int isColorTerm(void) {
}
/* Helpe function for sdsCatColorizedLdbReply() appending colorize strings
* to a SDS string. */
* to an SDS string. */
sds sdscatcolor(sds o, char *s, size_t len, char *color) {
if (!isColorTerm()) return sdscatlen(o,s,len);
@ -974,7 +974,7 @@ static void usage(void) {
" Default time interval is 1 sec. Change it using -i.\n"
" --lru-test <keys> Simulate a cache workload with an 80-20 distribution.\n"
" --slave Simulate a slave showing commands received from the master.\n"
" --rdb <filename> Transfer a RDB dump from remote server to local file.\n"
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
" --pipe Transfer raw Redis protocol from stdin to server.\n"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
" no reply is received within <n> seconds.\n"
@ -1200,7 +1200,7 @@ static int evalMode(int argc, char **argv) {
got_comma = 0;
keys = 0;
/* Load the script from the file, as a sds string. */
/* Load the script from the file, as an sds string. */
fp = fopen(config.eval,"r");
if (!fp) {
fprintf(stderr,
@ -1321,7 +1321,7 @@ struct distsamples {
};
/* Helper function for latencyDistMode(). Performs the spectrum visualization
* of the collected samples targeting a xterm 256 terminal.
* of the collected samples targeting an xterm 256 terminal.
*
* Takes an array of distsamples structures, ordered from smaller to bigger
* 'max' value. Last sample max must be 0, to mean that it olds all the

View File

@ -654,7 +654,7 @@ void syncCommand(client *c) {
} else if (server.rdb_child_pid != -1 &&
server.rdb_child_type == RDB_CHILD_TYPE_SOCKET)
{
/* There is a RDB child process but it is writing directly to
/* There is an RDB child process but it is writing directly to
* children sockets. We need to wait for the next BGSAVE
* in order to synchronize. */
serverLog(LL_NOTICE,"Waiting for next BGSAVE for SYNC");
@ -862,9 +862,9 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
} else if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {
struct redis_stat buf;
/* If this was a RDB on disk save, we have to prepare to send
/* If this was an RDB on disk save, we have to prepare to send
* the RDB from disk to the slave socket. Otherwise if this was
* already a RDB -> Slaves socket transfer, used in the case of
* already an RDB -> Slaves socket transfer, used in the case of
* diskless replication, our work is trivial, we can just put
* the slave online. */
if (type == RDB_CHILD_TYPE_SOCKET) {
@ -1155,7 +1155,7 @@ error:
/* Send a synchronous command to the master. Used to send AUTH and
* REPLCONF commands before starting the replication with SYNC.
*
* The command returns a sds string representing the result of the
* The command returns an sds string representing the result of the
* operation. On error the first byte is a "-".
*/
#define SYNC_CMD_READ (1<<0)

View File

@ -555,7 +555,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
* output buffers. */
if (listLength(c->reply) == 0 && c->bufpos < PROTO_REPLY_CHUNK_BYTES) {
/* This is a fast path for the common case of a reply inside the
* client static buffer. Don't create a SDS string but just use
* client static buffer. Don't create an SDS string but just use
* the client buffer directly. */
c->buf[c->bufpos] = '\0';
reply = c->buf;
@ -619,7 +619,7 @@ cleanup:
if (raise_error) {
/* If we are here we should have an error in the stack, in the
* form of a table with a "err" field. Extract the string to
* form of a table with an "err" field. Extract the string to
* return the plain error. */
inuse--;
return luaRaiseError(lua);
@ -1637,7 +1637,7 @@ int ldbStartSession(client *c) {
/* End a debugging session after the EVAL call with debugging enabled
* returned. */
void ldbEndSession(client *c) {
/* Emit the remaining logs and a <endsession> mark. */
/* Emit the remaining logs and an <endsession> mark. */
ldbLog(sdsnew("<endsession>"));
ldbSendLogs();

View File

@ -71,7 +71,7 @@ static inline char sdsReqType(size_t string_size) {
* If NULL is used for 'init' the string is initialized with zero bytes.
*
* The string is always null-termined (all the sds strings are, always) so
* even if you create a sds string with:
* even if you create an sds string with:
*
* mystring = sdsnewlen("abc",3);
*
@ -146,12 +146,12 @@ sds sdsnew(const char *init) {
return sdsnewlen(init, initlen);
}
/* Duplicate a sds string. */
/* Duplicate an sds string. */
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
/* Free a sds string. No operation is performed if 's' is NULL. */
/* Free an sds string. No operation is performed if 's' is NULL. */
void sdsfree(sds s) {
if (s == NULL) return;
s_free((char*)s-sdsHdrSize(s[-1]));
@ -176,7 +176,7 @@ void sdsupdatelen(sds s) {
sdssetlen(s, reallen);
}
/* Modify a sds string in-place to make it empty (zero length).
/* Modify an sds string in-place to make it empty (zero length).
* However all the existing buffer is not discarded but set as free space
* so that next append operations will not require allocations up to the
* number of bytes previously available. */
@ -486,7 +486,7 @@ int sdsull2str(char *s, unsigned long long v) {
return l;
}
/* Create a sds string from a long long value. It is much faster than:
/* Create an sds string from a long long value. It is much faster than:
*
* sdscatprintf(sdsempty(),"%lld\n", value);
*/
@ -1064,7 +1064,7 @@ sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
}
/* Join an array of C strings using the specified separator (also a C string).
* Returns the result as a sds string. */
* Returns the result as an sds string. */
sds sdsjoin(char **argv, int argc, char *sep) {
sds join = sdsempty();
int j;

View File

@ -403,7 +403,7 @@ mstime_t mstime(void) {
return ustime()/1000;
}
/* After a RDB dump or AOF rewrite we exit from children using _exit() instead of
/* After an RDB dump or AOF rewrite we exit from children using _exit() instead of
* exit(), because the latter may interact with the same file objects used by
* the parent process. However if we are testing the coverage normal exit() is
* used in order to obtain the right coverage information. */
@ -962,7 +962,7 @@ int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
return 0;
}
/* The client query buffer is a sds.c string that can end with a lot of
/* The client query buffer is an sds.c string that can end with a lot of
* free space not used, this function reclaims space if needed.
*
* The function always returns 0 as it never terminates the client. */
@ -1118,7 +1118,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
}
/* We have just LRU_BITS bits per object for LRU information.
* So we use a (eventually wrapping) LRU clock.
* So we use an (eventually wrapping) LRU clock.
*
* Note that even if the counter wraps it's not a big problem,
* everything will still work but some object will appear younger
@ -2104,7 +2104,7 @@ struct redisCommand *lookupCommandOrOriginal(sds name) {
/* Propagate the specified command (in the context of the specified database id)
* to AOF and Slaves.
*
* flags are a xor between:
* flags are an xor between:
* + PROPAGATE_NONE (no propagation of command at all)
* + PROPAGATE_AOF (propagate into the AOF file if is enabled)
* + PROPAGATE_REPL (propagate into the replication link)
@ -2544,7 +2544,7 @@ int prepareForShutdown(int flags) {
We want to avoid race conditions, for instance our saving child may
overwrite the synchronous saving did by SHUTDOWN. */
if (server.rdb_child_pid != -1) {
serverLog(LL_WARNING,"There is a child saving a .rdb. Killing it!");
serverLog(LL_WARNING,"There is a child saving an .rdb. Killing it!");
kill(server.rdb_child_pid,SIGUSR1);
rdbRemoveTempFile(server.rdb_child_pid);
}
@ -3995,7 +3995,7 @@ int main(int argc, char **argv) {
/* Check if we need to start in redis-check-rdb mode. We just execute
* the program main. However the program is part of the Redis executable
* so that we can easily execute a RDB check on loading errors. */
* so that we can easily execute an RDB check on loading errors. */
if (strstr(argv[0],"redis-check-rdb") != NULL)
exit(redis_check_rdb_main(argv,argc));

View File

@ -222,7 +222,7 @@ void sortCommand(client *c) {
else
sortval = createQuicklistObject();
/* The SORT command has a SQL-alike syntax, parse it */
/* The SORT command has an SQL-alike syntax, parse it */
while(j < c->argc) {
int leftargs = c->argc-j-1;
if (!strcasecmp(c->argv[j]->ptr,"asc")) {

View File

@ -629,7 +629,7 @@ void hincrbyfloatCommand(client *c) {
notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
server.dirty++;
/* Always replicate HINCRBYFLOAT as a HSET command with the final value
/* Always replicate HINCRBYFLOAT as an HSET command with the final value
* in order to make sure that differences in float pricision or formatting
* will not create differences in replicas or after an AOF restart. */
robj *aux, *newobj;

View File

@ -898,7 +898,7 @@ void blockingPopGenericCommand(client *c, int where) {
signalModifiedKey(c->db,c->argv[j]);
server.dirty++;
/* Replicate it as a [LR]POP instead of B[LR]POP. */
/* Replicate it as an [LR]POP instead of B[LR]POP. */
rewriteClientCommandVector(c,2,
(where == LIST_HEAD) ? shared.lpop : shared.rpop,
c->argv[j]);

View File

@ -426,7 +426,7 @@ void spopWithCountCommand(client *c) {
size = setTypeSize(set);
/* Generate a SPOP keyspace notification */
/* Generate an SPOP keyspace notification */
notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id);
server.dirty += count;
@ -484,7 +484,7 @@ void spopWithCountCommand(client *c) {
setTypeRemove(set,sdsele);
}
/* Replicate/AOF this command as a SREM operation */
/* Replicate/AOF this command as an SREM operation */
propargv[2] = objele;
alsoPropagate(server.sremCommand,c->db->id,propargv,3,
PROPAGATE_AOF|PROPAGATE_REPL);
@ -531,7 +531,7 @@ void spopWithCountCommand(client *c) {
objele = createStringObject(sdsele,sdslen(sdsele));
}
/* Replicate/AOF this command as a SREM operation */
/* Replicate/AOF this command as an SREM operation */
propargv[2] = objele;
alsoPropagate(server.sremCommand,c->db->id,propargv,3,
PROPAGATE_AOF|PROPAGATE_REPL);
@ -542,7 +542,7 @@ void spopWithCountCommand(client *c) {
}
/* Don't propagate the command itself even if we incremented the
* dirty counter. We don't want to propagate a SPOP command since
* dirty counter. We don't want to propagate an SPOP command since
* we propagated the command as a set of SREMs operations using
* the alsoPropagate() API. */
decrRefCount(propargv[0]);
@ -582,7 +582,7 @@ void spopCommand(client *c) {
notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id);
/* Replicate/AOF this command as a SREM operation */
/* Replicate/AOF this command as an SREM operation */
aux = createStringObject("SREM",4);
rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
decrRefCount(aux);
@ -979,7 +979,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
}
/* We need a temp set object to store our union. If the dstkey
* is not NULL (that is, we are inside a SUNIONSTORE operation) then
* is not NULL (that is, we are inside an SUNIONSTORE operation) then
* this set object will be the resulting object to set into the target key*/
dstset = createIntsetObject();

View File

@ -444,7 +444,7 @@ void appendCommand(client *c) {
if (checkType(c,o,OBJ_STRING))
return;
/* "append" is an argument, so always a sds */
/* "append" is an argument, so always an sds */
append = c->argv[2];
totlen = stringObjectLen(o)+sdslen(append->ptr);
if (checkStringLength(c,totlen) != C_OK)

View File

@ -686,7 +686,7 @@ double zzlGetScore(unsigned char *sptr) {
return score;
}
/* Return a ziplist element as a SDS string. */
/* Return a ziplist element as an SDS string. */
sds ziplistGetObject(unsigned char *sptr) {
unsigned char *vstr;
unsigned int vlen;

View File

@ -613,7 +613,7 @@ void getRandomHexChars(char *p, unsigned int len) {
}
}
/* Given the filename, return the absolute path as a SDS string, or NULL
/* Given the filename, return the absolute path as an SDS string, or NULL
* if it fails for some reason. Note that "filename" may be an absolute path
* already, this will be detected and handled correctly.
*