Factor out time formatting functions.
[cumulus.git] / util.cc
1 /* Cumulus: Efficient Filesystem Backup to the Cloud
2  * Copyright (C) 2007-2008 The Cumulus Developers
3  * See the AUTHORS file for a list of contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 /* Utility functions for converting various datatypes to text format (and
21  * later, for parsing them back, perhaps). */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <uuid/uuid.h>
28
29 #include <iostream>
30 #include <map>
31 #include <string>
32 #include <sstream>
33
34 #include "util.h"
35
36 using std::map;
37 using std::ostream;
38 using std::string;
39
40 /* Perform URI-style escaping of a string.  Bytes which cannot be represented
41  * directly are encoded in the form %xx (where "xx" is a string of two
42  * hexadecimal digits). */
43 string uri_encode(const string &in)
44 {
45     string out;
46
47     for (size_t i = 0; i < in.length(); i++) {
48         unsigned char c = in[i];
49
50         if (c >= '+' && c < 0x7f && c != '@') {
51             out += c;
52         } else {
53             char buf[4];
54             sprintf(buf, "%%%02x", c);
55             out += buf;
56         }
57     }
58
59     return out;
60 }
61
62 /* Decoding of strings produced by uri_encode. */
63 string uri_decode(const string &in)
64 {
65     char *buf = new char[in.size() + 1];
66
67     const char *input = in.c_str();
68     char *output = buf;
69
70     while (*input != '\0') {
71         if (*input == '%') {
72             char hexbuf[4];
73             if (isxdigit(input[1]) && isxdigit(input[2])) {
74                 hexbuf[0] = input[1];
75                 hexbuf[1] = input[2];
76                 hexbuf[2] = '\0';
77                 *output++ = strtol(hexbuf, NULL, 16);
78                 input += 3;
79             } else {
80                 input++;
81             }
82         } else {
83             *output++ = *input++;
84         }
85     }
86
87     *output = '\0';
88
89     string result(buf);
90     delete[] buf;
91     return result;
92 }
93
94 /* Return the string representation of an integer.  Will try to produce output
95  * in decimal, hexadecimal, or octal according to base, though this is just
96  * advisory.  For negative numbers, will always use decimal. */
97 string encode_int(long long n, int base)
98 {
99     char buf[64];
100
101     if (n >= 0 && base == 16) {
102         sprintf(buf, "0x%llx", n);
103         return buf;
104     }
105
106     if (n > 0 && base == 8) {
107         sprintf(buf, "0%llo", n);
108         return buf;
109     }
110
111     sprintf(buf, "%lld", n);
112     return buf;
113 }
114
115 /* Parse the string representation of an integer.  Accepts decimal, octal, and
116  * hexadecimal, just as C would (recognizes the 0 and 0x prefixes). */
117 long long parse_int(const string &s)
118 {
119     return strtoll(s.c_str(), NULL, 0);
120 }
121
122 /* Mark a file descriptor as close-on-exec. */
123 void cloexec(int fd)
124 {
125     long flags = fcntl(fd, F_GETFD);
126
127     if (flags < 0)
128         return;
129
130     fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
131 }
132
133 /* Report a fatal error and exit. */
134 void fatal(string msg)
135 {
136     fprintf(stderr, "FATAL: %s\n", msg.c_str());
137     exit(1);
138 }
139
140 /* Available time formats. */
141 const char TimeFormat::FORMAT_FILENAME[] = "%Y%m%dT%H%M%S";
142 const char TimeFormat::FORMAT_ISO8601[] = "%Y-%m-%d %H:%M:%S";
143 const char TimeFormat::FORMAT_LOCALTIME[] = "%Y-%m-%d %H:%M:%S %z";
144
145 static size_t MAX_TIMESTAMP_LENGTH = 1024;
146
147 std::string TimeFormat::format(time_t timestamp, const char *format, bool utc)
148 {
149     struct tm time_buf;
150
151     if (utc)
152         gmtime_r(&timestamp, &time_buf);
153     else
154         localtime_r(&timestamp, &time_buf);
155
156     char buffer[MAX_TIMESTAMP_LENGTH];
157     strftime(buffer, MAX_TIMESTAMP_LENGTH, format, &time_buf);
158     return string(buffer);
159 }