Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include "osl/file.h"
31 : : #include "osl/detail/file.h"
32 : :
33 : : #include "system.h"
34 : : #include <sys/types.h>
35 : : #include <dirent.h>
36 : : #include <errno.h>
37 : : #include <limits.h>
38 : : #include <unistd.h>
39 : :
40 : : #include "file_impl.hxx"
41 : : #include "file_error_transl.h"
42 : : #include "file_path_helper.hxx"
43 : : #include "file_url.h"
44 : : #include "uunxapi.hxx"
45 : :
46 : : namespace /* private */
47 : : {
48 : :
49 : 79600 : inline void set_file_type(const struct stat& file_stat, oslFileStatus* pStat)
50 : : {
51 : : /* links to directories state also to be a directory */
52 [ + + ]: 79600 : if (S_ISLNK(file_stat.st_mode))
53 : 1808 : pStat->eType = osl_File_Type_Link;
54 [ + + ]: 77792 : else if (S_ISDIR(file_stat.st_mode))
55 : 28199 : pStat->eType = osl_File_Type_Directory;
56 [ + - ]: 49593 : else if (S_ISREG(file_stat.st_mode))
57 : 49593 : pStat->eType = osl_File_Type_Regular;
58 [ # # ]: 0 : else if (S_ISFIFO(file_stat.st_mode))
59 : 0 : pStat->eType = osl_File_Type_Fifo;
60 [ # # ]: 0 : else if (S_ISSOCK(file_stat.st_mode))
61 : 0 : pStat->eType = osl_File_Type_Socket;
62 [ # # ][ # # ]: 0 : else if (S_ISCHR(file_stat.st_mode) || S_ISBLK(file_stat.st_mode))
63 : 0 : pStat->eType = osl_File_Type_Special;
64 : : else
65 : 0 : pStat->eType = osl_File_Type_Unknown;
66 : :
67 : 79600 : pStat->uValidFields |= osl_FileStatus_Mask_Type;
68 : 79600 : }
69 : :
70 : 79600 : inline void set_file_access_mask(const struct stat& file_stat, oslFileStatus* pStat)
71 : : {
72 : : // user permissions
73 [ + - ]: 79600 : if (S_IRUSR & file_stat.st_mode)
74 : 79600 : pStat->uAttributes |= osl_File_Attribute_OwnRead;
75 : :
76 [ + + ]: 79600 : if (S_IWUSR & file_stat.st_mode)
77 : 75322 : pStat->uAttributes |= osl_File_Attribute_OwnWrite;
78 : :
79 [ + + ]: 79600 : if (S_IXUSR & file_stat.st_mode)
80 : 31833 : pStat->uAttributes |= osl_File_Attribute_OwnExe;
81 : :
82 : : // group permissions
83 [ + + ]: 79600 : if (S_IRGRP & file_stat.st_mode)
84 : 72988 : pStat->uAttributes |= osl_File_Attribute_GrpRead;
85 : :
86 [ + + ]: 79600 : if (S_IWGRP & file_stat.st_mode)
87 : 3103 : pStat->uAttributes |= osl_File_Attribute_GrpWrite;
88 : :
89 [ + + ]: 79600 : if (S_IXGRP & file_stat.st_mode)
90 : 31596 : pStat->uAttributes |= osl_File_Attribute_GrpExe;
91 : :
92 : : // others permissions
93 [ + + ]: 79600 : if (S_IROTH & file_stat.st_mode)
94 : 72988 : pStat->uAttributes |= osl_File_Attribute_OthRead;
95 : :
96 [ + + ]: 79600 : if (S_IWOTH & file_stat.st_mode)
97 : 3041 : pStat->uAttributes |= osl_File_Attribute_OthWrite;
98 : :
99 [ + + ]: 79600 : if (S_IXOTH & file_stat.st_mode)
100 : 30241 : pStat->uAttributes |= osl_File_Attribute_OthExe;
101 : :
102 : 79600 : pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
103 : 79600 : }
104 : :
105 : : /* This code used not to use access(...) because access follows links which
106 : : may cause performance problems see #97133. (That apparently references a
107 : : no-longer accessible Hamburg-internal bug-tracking system.)
108 : : However, contrary to what is stated above the use of access calls is
109 : : required on network file systems not using unix semantics (AFS, see
110 : : fdo#43095).
111 : : */
112 : 8777 : inline void set_file_access_rights(const rtl::OUString& file_path, oslFileStatus* pStat)
113 : : {
114 : 8777 : pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
115 : :
116 [ - + ]: 8777 : if (access_u(file_path.pData, W_OK) < 0)
117 : 0 : pStat->uAttributes |= osl_File_Attribute_ReadOnly;
118 : :
119 [ + + ]: 8777 : if (access_u(file_path.pData, X_OK) == 0)
120 : 2542 : pStat->uAttributes |= osl_File_Attribute_Executable;
121 : 8777 : }
122 : :
123 : :
124 : 79600 : inline void set_file_hidden_status(const rtl::OUString& file_path, oslFileStatus* pStat)
125 : : {
126 [ + + ]: 79600 : pStat->uAttributes = osl::systemPathIsHiddenFileOrDirectoryEntry(file_path) ? osl_File_Attribute_Hidden : 0;
127 : 79600 : pStat->uValidFields |= osl_FileStatus_Mask_Attributes;
128 : 79600 : }
129 : :
130 : : /* the set_file_access_rights must be called after set_file_hidden_status(...) and
131 : : set_file_access_mask(...) because of the hack in set_file_access_rights(...) */
132 : 79600 : inline void set_file_attributes(
133 : : const rtl::OUString& file_path, const struct stat& file_stat, const sal_uInt32 uFieldMask, oslFileStatus* pStat)
134 : : {
135 : 79600 : set_file_hidden_status(file_path, pStat);
136 : 79600 : set_file_access_mask(file_stat, pStat);
137 : :
138 : : // we set the file access rights only on demand
139 : : // because it's potentially expensive
140 [ + + ]: 79600 : if (uFieldMask & osl_FileStatus_Mask_Attributes)
141 : 8777 : set_file_access_rights(file_path, pStat);
142 : 79600 : }
143 : :
144 : 79600 : inline void set_file_access_time(const struct stat& file_stat, oslFileStatus* pStat)
145 : : {
146 : 79600 : pStat->aAccessTime.Seconds = file_stat.st_atime;
147 : 79600 : pStat->aAccessTime.Nanosec = 0;
148 : 79600 : pStat->uValidFields |= osl_FileStatus_Mask_AccessTime;
149 : 79600 : }
150 : :
151 : 79600 : inline void set_file_modify_time(const struct stat& file_stat, oslFileStatus* pStat)
152 : : {
153 : 79600 : pStat->aModifyTime.Seconds = file_stat.st_mtime;
154 : 79600 : pStat->aModifyTime.Nanosec = 0;
155 : 79600 : pStat->uValidFields |= osl_FileStatus_Mask_ModifyTime;
156 : 79600 : }
157 : :
158 : 79600 : inline void set_file_size(const struct stat& file_stat, oslFileStatus* pStat)
159 : : {
160 [ + + ]: 79600 : if (S_ISREG(file_stat.st_mode))
161 : : {
162 : 49593 : pStat->uFileSize = file_stat.st_size;
163 : 49593 : pStat->uValidFields |= osl_FileStatus_Mask_FileSize;
164 : : }
165 : 79600 : }
166 : :
167 : : /* we only need to call stat or lstat if one of the
168 : : following flags is set */
169 : 303283 : inline bool is_stat_call_necessary(sal_uInt32 field_mask, oslFileType file_type = osl_File_Type_Unknown)
170 : : {
171 : : return (
172 : : ((field_mask & osl_FileStatus_Mask_Type) && (file_type == osl_File_Type_Unknown)) ||
173 : : (field_mask & osl_FileStatus_Mask_Attributes) ||
174 : : (field_mask & osl_FileStatus_Mask_CreationTime) ||
175 : : (field_mask & osl_FileStatus_Mask_AccessTime) ||
176 : : (field_mask & osl_FileStatus_Mask_ModifyTime) ||
177 : : (field_mask & osl_FileStatus_Mask_FileSize) ||
178 : : (field_mask & osl_FileStatus_Mask_LinkTargetURL) ||
179 [ + + ][ + + ]: 303283 : (field_mask & osl_FileStatus_Mask_Validate));
[ + + ][ + - ]
[ + - ][ + + ]
[ + + ][ + + ]
[ + + ]
180 : : }
181 : :
182 : 1808 : inline oslFileError set_link_target_url(const rtl::OUString& file_path, oslFileStatus* pStat)
183 : : {
184 : 1808 : rtl::OUString link_target;
185 [ - + ][ + - ]: 1808 : if (!osl::realpath(file_path, link_target))
186 [ # # ]: 0 : return oslTranslateFileError(OSL_FET_ERROR, errno);
187 : :
188 [ + - ]: 1808 : oslFileError osl_error = osl_getFileURLFromSystemPath(link_target.pData, &pStat->ustrLinkTargetURL);
189 [ - + ]: 1808 : if (osl_error != osl_File_E_None)
190 : 0 : return osl_error;
191 : :
192 : 1808 : pStat->uValidFields |= osl_FileStatus_Mask_LinkTargetURL;
193 : 1808 : return osl_File_E_None;
194 : : }
195 : :
196 : 315705 : inline oslFileError setup_osl_getFileStatus(
197 : : DirectoryItem_Impl * pImpl, oslFileStatus* pStat, rtl::OUString& file_path)
198 : : {
199 [ + + ][ - + ]: 315705 : if ((NULL == pImpl) || (NULL == pStat))
200 : 12422 : return osl_File_E_INVAL;
201 : :
202 : 303283 : file_path = rtl::OUString(pImpl->m_ustrFilePath);
203 : : OSL_ASSERT(!file_path.isEmpty());
204 [ - + ]: 303283 : if (file_path.isEmpty())
205 : 0 : return osl_File_E_INVAL;
206 : :
207 : 303283 : pStat->uValidFields = 0;
208 : 315705 : return osl_File_E_None;
209 : : }
210 : :
211 : : } // end namespace private
212 : :
213 : :
214 : : /****************************************************************************
215 : : * osl_getFileStatus
216 : : ****************************************************************************/
217 : :
218 : 315705 : oslFileError SAL_CALL osl_getFileStatus(oslDirectoryItem Item, oslFileStatus* pStat, sal_uInt32 uFieldMask)
219 : : {
220 : 315705 : DirectoryItem_Impl * pImpl = static_cast< DirectoryItem_Impl* >(Item);
221 : :
222 : 315705 : rtl::OUString file_path;
223 : 315705 : oslFileError osl_error = setup_osl_getFileStatus(pImpl, pStat, file_path);
224 [ + + ]: 315705 : if (osl_File_E_None != osl_error)
225 : 12422 : return osl_error;
226 : :
227 : : struct stat file_stat;
228 : :
229 [ + - ]: 303283 : bool bStatNeeded = is_stat_call_necessary(uFieldMask, pImpl->getFileType());
230 [ + - ][ - + ]: 303283 : if (bStatNeeded && (0 != osl::lstat(file_path, file_stat)))
[ - + ][ + + ]
231 [ # # ]: 0 : return oslTranslateFileError(OSL_FET_ERROR, errno);
232 : :
233 [ + + ]: 303283 : if (bStatNeeded)
234 : : {
235 : : // we set all these attributes because it's cheap
236 : 79600 : set_file_type(file_stat, pStat);
237 : 79600 : set_file_access_time(file_stat, pStat);
238 : 79600 : set_file_modify_time(file_stat, pStat);
239 : 79600 : set_file_size(file_stat, pStat);
240 [ + - ]: 79600 : set_file_attributes(file_path, file_stat, uFieldMask, pStat);
241 : :
242 : : // file exists semantic of osl_FileStatus_Mask_Validate
243 [ + + ][ + + ]: 79600 : if ((uFieldMask & osl_FileStatus_Mask_LinkTargetURL) && S_ISLNK(file_stat.st_mode))
244 : : {
245 [ + - ]: 1808 : osl_error = set_link_target_url(file_path, pStat);
246 [ - + ]: 1808 : if (osl_error != osl_File_E_None)
247 : 0 : return osl_error;
248 : : }
249 : : }
250 : : #ifdef _DIRENT_HAVE_D_TYPE
251 [ + + ]: 223683 : else if (uFieldMask & osl_FileStatus_Mask_Type)
252 : : {
253 [ + - ]: 66757 : pStat->eType = pImpl->getFileType();
254 : 66757 : pStat->uValidFields |= osl_FileStatus_Mask_Type;
255 : : }
256 : : #endif /* _DIRENT_HAVE_D_TYPE */
257 : :
258 [ + + ]: 303283 : if (uFieldMask & osl_FileStatus_Mask_FileURL)
259 : : {
260 [ + - ][ - + ]: 141844 : if ((osl_error = osl_getFileURLFromSystemPath(file_path.pData, &pStat->ustrFileURL)) != osl_File_E_None)
261 : 0 : return osl_error;
262 : :
263 : 141844 : pStat->uValidFields |= osl_FileStatus_Mask_FileURL;
264 : : }
265 : :
266 [ + + ]: 303283 : if (uFieldMask & osl_FileStatus_Mask_FileName)
267 : : {
268 [ + - ]: 158953 : osl_systemPathGetFileNameOrLastDirectoryPart(file_path.pData, &pStat->ustrFileName);
269 : 158953 : pStat->uValidFields |= osl_FileStatus_Mask_FileName;
270 : : }
271 : 315705 : return osl_File_E_None;
272 : : }
273 : :
274 : : /****************************************************************************/
275 : : /* osl_setFileAttributes */
276 : : /****************************************************************************/
277 : :
278 : 37280 : static oslFileError osl_psz_setFileAttributes( const sal_Char* pszFilePath, sal_uInt64 uAttributes )
279 : : {
280 : 37280 : oslFileError osl_error = osl_File_E_None;
281 : 37280 : mode_t nNewMode = 0;
282 : :
283 : : OSL_ENSURE(!(osl_File_Attribute_Hidden & uAttributes), "osl_File_Attribute_Hidden doesn't work under Unix");
284 : :
285 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_OwnRead)
286 : 37110 : nNewMode |= S_IRUSR;
287 : :
288 [ + - ]: 37280 : if (uAttributes & osl_File_Attribute_OwnWrite)
289 : 37280 : nNewMode|=S_IWUSR;
290 : :
291 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_OwnExe)
292 : 124 : nNewMode|=S_IXUSR;
293 : :
294 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_GrpRead)
295 : 36841 : nNewMode|=S_IRGRP;
296 : :
297 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_GrpWrite)
298 : 36132 : nNewMode|=S_IWGRP;
299 : :
300 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_GrpExe)
301 : 62 : nNewMode|=S_IXGRP;
302 : :
303 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_OthRead)
304 : 36841 : nNewMode|=S_IROTH;
305 : :
306 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_OthWrite)
307 : 170 : nNewMode|=S_IWOTH;
308 : :
309 [ + + ]: 37280 : if (uAttributes & osl_File_Attribute_OthExe)
310 : 62 : nNewMode|=S_IXOTH;
311 : :
312 [ - + ]: 37280 : if (chmod(pszFilePath, nNewMode) < 0)
313 : 0 : osl_error = oslTranslateFileError(OSL_FET_ERROR, errno);
314 : :
315 : 37280 : return osl_error;
316 : : }
317 : :
318 : 37280 : oslFileError SAL_CALL osl_setFileAttributes( rtl_uString* ustrFileURL, sal_uInt64 uAttributes )
319 : : {
320 : : char path[PATH_MAX];
321 : : oslFileError eRet;
322 : :
323 : : OSL_ASSERT( ustrFileURL );
324 : :
325 : : /* convert file url to system path */
326 [ + - ]: 37280 : eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
327 [ - + ]: 37280 : if( eRet != osl_File_E_None )
328 : 0 : return eRet;
329 : :
330 : : #ifdef MACOSX
331 : : if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
332 : : return oslTranslateFileError( OSL_FET_ERROR, errno );
333 : : #endif/* MACOSX */
334 : :
335 [ + - ]: 37280 : return osl_psz_setFileAttributes( path, uAttributes );
336 : : }
337 : :
338 : : /****************************************************************************/
339 : : /* osl_setFileTime */
340 : : /****************************************************************************/
341 : :
342 : 0 : static oslFileError osl_psz_setFileTime (
343 : : const sal_Char* pszFilePath,
344 : : const TimeValue* pLastAccessTime,
345 : : const TimeValue* pLastWriteTime )
346 : : {
347 : 0 : int nRet=0;
348 : : struct utimbuf aTimeBuffer;
349 : : struct stat aFileStat;
350 : : #ifdef DEBUG_OSL_FILE
351 : : struct tm* pTM=0;
352 : : #endif
353 : :
354 : 0 : nRet = lstat(pszFilePath,&aFileStat);
355 : :
356 [ # # ]: 0 : if ( nRet < 0 )
357 : : {
358 : 0 : nRet=errno;
359 [ # # ]: 0 : return oslTranslateFileError(OSL_FET_ERROR, nRet);
360 : : }
361 : :
362 : : #ifdef DEBUG_OSL_FILE
363 : : fprintf(stderr,"File Times are (in localtime):\n");
364 : : pTM=localtime(&aFileStat.st_ctime);
365 : : fprintf(stderr,"CreationTime is '%s'\n",asctime(pTM));
366 : : pTM=localtime(&aFileStat.st_atime);
367 : : fprintf(stderr,"AccessTime is '%s'\n",asctime(pTM));
368 : : pTM=localtime(&aFileStat.st_mtime);
369 : : fprintf(stderr,"Modification is '%s'\n",asctime(pTM));
370 : :
371 : : fprintf(stderr,"File Times are (in UTC):\n");
372 : : fprintf(stderr,"CreationTime is '%s'\n",ctime(&aFileStat.st_ctime));
373 : : fprintf(stderr,"AccessTime is '%s'\n",ctime(&aTimeBuffer.actime));
374 : : fprintf(stderr,"Modification is '%s'\n",ctime(&aTimeBuffer.modtime));
375 : : #endif
376 : :
377 [ # # ]: 0 : if ( pLastAccessTime != 0 )
378 : : {
379 : 0 : aTimeBuffer.actime=pLastAccessTime->Seconds;
380 : : }
381 : : else
382 : : {
383 : 0 : aTimeBuffer.actime=aFileStat.st_atime;
384 : : }
385 : :
386 [ # # ]: 0 : if ( pLastWriteTime != 0 )
387 : : {
388 : 0 : aTimeBuffer.modtime=pLastWriteTime->Seconds;
389 : : }
390 : : else
391 : : {
392 : 0 : aTimeBuffer.modtime=aFileStat.st_mtime;
393 : : }
394 : :
395 : : /* mfe: Creation time not used here! */
396 : :
397 : : #ifdef DEBUG_OSL_FILE
398 : : fprintf(stderr,"File Times are (in localtime):\n");
399 : : pTM=localtime(&aFileStat.st_ctime);
400 : : fprintf(stderr,"CreationTime now '%s'\n",asctime(pTM));
401 : : pTM=localtime(&aTimeBuffer.actime);
402 : : fprintf(stderr,"AccessTime now '%s'\n",asctime(pTM));
403 : : pTM=localtime(&aTimeBuffer.modtime);
404 : : fprintf(stderr,"Modification now '%s'\n",asctime(pTM));
405 : :
406 : : fprintf(stderr,"File Times are (in UTC):\n");
407 : : fprintf(stderr,"CreationTime now '%s'\n",ctime(&aFileStat.st_ctime));
408 : : fprintf(stderr,"AccessTime now '%s'\n",ctime(&aTimeBuffer.actime));
409 : : fprintf(stderr,"Modification now '%s'\n",ctime(&aTimeBuffer.modtime));
410 : : #endif
411 : :
412 : 0 : nRet=utime(pszFilePath,&aTimeBuffer);
413 [ # # ]: 0 : if ( nRet < 0 )
414 : : {
415 : 0 : nRet=errno;
416 [ # # ]: 0 : return oslTranslateFileError(OSL_FET_ERROR, nRet);
417 : : }
418 : :
419 : 0 : return osl_File_E_None;
420 : : }
421 : :
422 : 0 : oslFileError SAL_CALL osl_setFileTime (
423 : : rtl_uString* ustrFileURL,
424 : : SAL_UNUSED_PARAMETER const TimeValue* /* pCreationTime */,
425 : : const TimeValue* pLastAccessTime,
426 : : const TimeValue* pLastWriteTime )
427 : : {
428 : : char path[PATH_MAX];
429 : : oslFileError eRet;
430 : :
431 : : OSL_ASSERT( ustrFileURL );
432 : :
433 : : /* convert file url to system path */
434 [ # # ]: 0 : eRet = FileURLToPath( path, PATH_MAX, ustrFileURL );
435 [ # # ]: 0 : if( eRet != osl_File_E_None )
436 : 0 : return eRet;
437 : :
438 : : #ifdef MACOSX
439 : : if ( macxp_resolveAlias( path, PATH_MAX ) != 0 )
440 : : return oslTranslateFileError( OSL_FET_ERROR, errno );
441 : : #endif/* MACOSX */
442 : :
443 [ # # ]: 0 : return osl_psz_setFileTime( path, pLastAccessTime, pLastWriteTime );
444 : : }
445 : :
446 : : sal_Bool
447 : 0 : SAL_CALL osl_identicalDirectoryItem( oslDirectoryItem a, oslDirectoryItem b)
448 : : {
449 : 0 : DirectoryItem_Impl *pA = (DirectoryItem_Impl *) a;
450 : 0 : DirectoryItem_Impl *pB = (DirectoryItem_Impl *) b;
451 [ # # ]: 0 : if (a == b)
452 : 0 : return sal_True;
453 : : /* same name => same item, unless renaming / moving madness has occurred */
454 [ # # ]: 0 : if (rtl_ustr_compare_WithLength(
455 : : pA->m_ustrFilePath->buffer, pA->m_ustrFilePath->length,
456 : 0 : pB->m_ustrFilePath->buffer, pB->m_ustrFilePath->length ) == 0)
457 : 0 : return sal_True;
458 : :
459 : : struct stat a_stat, b_stat;
460 : :
461 [ # # ][ # # ]: 0 : if (osl::lstat(rtl::OUString(pA->m_ustrFilePath), a_stat) != 0 ||
[ # # ][ # # ]
[ # # # # ]
462 [ # # ][ # # ]: 0 : osl::lstat(rtl::OUString(pB->m_ustrFilePath), b_stat) != 0)
[ # # ]
463 : 0 : return sal_False;
464 : :
465 : 0 : return (a_stat.st_ino == b_stat.st_ino);
466 : : }
467 : :
468 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|