/**************************************************************************** * libs/libc/stdlib/lib_mktemp.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-processor definitions ****************************************************************************/ #define MAX_XS 6 #define MIN_NUMERIC 0 /* 0-9: Numeric */ #define MAX_NUMERIC 9 #define MIN_UPPERCASE 10 /* 10-35: Upper case */ #define MAX_UPPERCASE 35 #define MIN_LOWERCASE 36 /* 36-61: Lower case */ #define MAX_LOWERCASE 61 #define MAX_BASE62 MAX_LOWERCASE /* 62**1 = 62 * 62**2 = 3844 * 62**3 = 238328 * 62**4 = 14776336 * 62**5 = 916132832 * 62**6 = 56800235584 > UINT32_MAX */ #define BIG_XS 5 /**************************************************************************** * Private Data ****************************************************************************/ static uint8_t g_base62[MAX_XS]; static sem_t g_b62sem = SEM_INITIALIZER(1); /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: base62_to_char * * Description: * Convert a base62 value to a printable character. * ****************************************************************************/ static char base62_to_char(uint8_t base62) { if (base62 <= MAX_NUMERIC) { return '0' + base62; } else if (base62 <= MAX_UPPERCASE) { return 'A' + base62 - MIN_UPPERCASE; } else /* if (base62 <= MAX_LOWERCASE) */ { DEBUGASSERT(base62 <= MAX_LOWERCASE); return 'a' + base62 - MIN_LOWERCASE; } } /**************************************************************************** * Name: incr_base62 * * Description: * increment the base62 value array. * ****************************************************************************/ static void incr_base62(void) { int i; for (i = MAX_XS - 1; i >= 0; i--) { if (g_base62[i] < MAX_LOWERCASE) { g_base62[i]++; return; } else { g_base62[i] = 0; } } } /**************************************************************************** * Name: get_base62 * * Description: * Atomically copy and increment the base62 array. * ****************************************************************************/ static void get_base62(FAR uint8_t *ptr) { int ret; while ((ret = _SEM_WAIT(&g_b62sem)) < 0) { DEBUGASSERT(_SEM_ERRNO(ret) == EINTR || _SEM_ERRNO(ret) == ECANCELED); } memcpy(ptr, g_base62, MAX_XS); incr_base62(); _SEM_POST(&g_b62sem); } /**************************************************************************** * Name: copy_base62 * * Description: * Copy the base62 array into the template filename, converting each * base62 value to a printable character. * ****************************************************************************/ static void copy_base62(FAR const uint8_t *src, FAR char *dest, int len) { if (len < MAX_XS) { src += MAX_XS - len; } for (; len > 0; len--) { *dest++ = base62_to_char(*src++); } } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: mktemp * * Description: * The mktemp() function generates a unique temporary filename from * template. The last six characters of template must be XXXXXX and these * are replaced with a string that makes the filename unique. Since it * will be modified, template must not be a string constant, but should be * declared as a character array. * ****************************************************************************/ FAR char *mktemp(FAR char *path_template) { uint8_t base62[MAX_XS]; uint32_t retries; struct stat buf; FAR char *xptr; int xlen; int ret; int i; /* Count the number of X's at the end of the template */ xptr = &path_template[strlen(path_template)]; for (xlen = 0; xlen < MAX_XS && path_template < xptr && *(xptr - 1) == 'X'; xlen++, xptr--); if (xlen == 0) { /* No Xs? There should always really be 6 */ return path_template; } /* Ignore any X's after the sixth */ if (xlen > MAX_XS) { xptr += xlen - MAX_XS; xlen = MAX_XS; } /* If xlen is small, then we need to determine the maximum number of * retries before the values will repeat. */ if (xlen >= BIG_XS) { retries = UINT32_MAX; } else { for (i = 1, retries = 62; i < xlen; i++, retries *= 62); } /* Then loop until we find a unique file name */ while (retries > 0) { /* Sample and increment the base62 counter */ get_base62(base62); /* Form the candidate file name */ copy_base62(base62, xptr, xlen); /* Attempt to stat the candidate file */ ret = stat(path_template, &buf); if (ret < 0 && get_errno() == ENOENT) { /* We have it... Clear the errno and return the template */ set_errno(0); return path_template; } retries--; } /* We could not find an unique filename */ set_errno(EINVAL); return NULL; }